MEF中仅获取到Worker方法,如何获取所有标记MethodGetterAttribute的方法?
你当前的代码只能拿到Worker的方法,核心问题出在MEF的导出合同匹配上:container.GetExports<Action<Worker>>()只会精准匹配签名为Action<Worker>的导出方法。另外两个GetName方法的参数分别是Model.First.Person和Model.First.Second.Person(即使类名相同,不同命名空间下也是完全不同的类型),对应的委托类型是Action<Model.First.Person>和Action<Model.First.Second.Person>,和Action<Worker>不属于同一合同,所以MEF不会返回它们。
要获取所有标记MethodGetterAttribute的方法,我们需要跳出“指定具体委托类型”的限制,改用统一的导出标识或者通用类型来获取所有相关导出,再通过反射处理执行逻辑。
方案1:为特性指定统一的导出合同名称
首先修改你的MethodGetterAttribute,在继承ExportAttribute时指定一个统一的合同名称,这样所有标记该特性的方法都会导出到同一个合同下:
[AttributeUsage(AttributeTargets.Method)] public class MethodGetterAttribute : ExportAttribute { // 指定统一的合同名称,比如"IMethodGetter" public MethodGetterAttribute() : base("IMethodGetter") { } }
然后在获取导出时,直接通过这个合同名称获取所有相关导出:
Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies(); var catalog = new AggregateCatalog(); catalog.Catalogs.Add(new AssemblyCatalog(assemblies.FirstOrDefault(a => a.GetName().Name.Contains("Model")))); var container = new CompositionContainer(catalog); // 通过统一合同名称获取所有标记MethodGetter的方法 var allMethodExports = container.GetExports<object>("IMethodGetter"); // 准备好各个方法需要的参数实例(根据你的实际业务创建) var firstPerson = new Model.First.Person(); var secondPerson = new Model.First.Second.Person(); var worker = new Model.First.Second.Third.Worker(); // 遍历所有导出,根据参数类型执行方法 foreach (var export in allMethodExports) { var action = export.Value; var actionType = action.GetType(); // 获取委托的泛型参数类型(即方法的参数类型) var parameterType = actionType.GetGenericArguments()[0]; // 根据参数类型匹配对应的实例并执行 if (parameterType == typeof(Model.First.Person)) { ((Action<Model.First.Person>)action).Invoke(firstPerson); } else if (parameterType == typeof(Model.First.Second.Person)) { ((Action<Model.First.Second.Person>)action).Invoke(secondPerson); } else if (parameterType == typeof(Model.First.Second.Third.Worker)) { ((Action<Model.First.Second.Third.Worker>)action).Invoke(worker); } }
方案2:不修改特性,直接筛选所有导出
如果你不想修改MethodGetterAttribute,可以直接获取所有导出,然后筛选出标记了该特性的方法:
Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies(); var catalog = new AggregateCatalog(); catalog.Catalogs.Add(new AssemblyCatalog(assemblies.FirstOrDefault(a => a.GetName().Name.Contains("Model")))); var container = new CompositionContainer(catalog); // 获取所有导出,筛选出标记了MethodGetterAttribute的委托方法 var allMethodExports = container.GetExports<object>() .Where(export => export.Value is Delegate && export.Value.GetType().GetCustomAttributes<MethodGetterAttribute>().Any()) .ToList(); // 准备参数实例 var firstPerson = new Model.First.Person(); var secondPerson = new Model.First.Second.Person(); var worker = new Model.First.Second.Third.Worker(); // 遍历执行方法,用DynamicInvoke简化类型转换 foreach (var export in allMethodExports) { var action = (Delegate)export.Value; var parameterType = action.GetType().GetGenericArguments()[0]; if (parameterType == typeof(Model.First.Person)) { action.DynamicInvoke(firstPerson); } else if (parameterType == typeof(Model.First.Second.Person)) { action.DynamicInvoke(secondPerson); } else if (parameterType == typeof(Model.First.Second.Third.Worker)) { action.DynamicInvoke(worker); } }
如果Model.First.Person和Model.First.Second.Person其实是同一个类(只是在不同命名空间下引用),那它们的完全限定名一致,Action<Person>的类型也会相同,此时你可以直接用GetExports<Action<Person>>()获取前两个方法,再单独获取Action<Worker>的方法,但从你的代码结构看,它们应该是不同的类,所以通用方案更稳妥。
内容的提问来源于stack exchange,提问作者Dilshod K

