在C#中是否有一种使用反射来确定方法是否已作为扩展方法添加到类的技术?
给定一个扩展方法,如下所示,可以确定Reverse()已被添加到字符串类?
public static class StringExtensions { public static string Reverse(this string value) { char[] cArray = value.ToCharArray(); Array.Reverse(cArray); return new string(cArray); } }
我们正在寻找一种机制,在单元测试中确定扩展方法是由开发人员适当添加的.尝试这种情况的一个原因是,开发人员可能会在实际的类中添加类似的方法,如果是,编译器将选择该方法.
您必须查看可以定义扩展方法的所有程序集.
查找用它装饰的类ExtensionAttribute
,然后在该类中使用也装饰的方法ExtensionAttribute
.然后检查第一个参数的类型,看它是否与您感兴趣的类型相匹配.
这是一些完整的代码.它可能更严格(它没有检查类型是否嵌套,或者至少有一个参数)但它应该给你一个帮助.
using System; using System.Runtime.CompilerServices; using System.Reflection; using System.Linq; using System.Collections.Generic; public static class FirstExtensions { public static void Foo(this string x) {} public static void Bar(string x) {} // Not an ext. method public static void Baz(this int x) {} // Not on string } public static class SecondExtensions { public static void Quux(this string x) {} } public class Test { static void Main() { Assembly thisAssembly = typeof(Test).Assembly; foreach (MethodInfo method in GetExtensionMethods(thisAssembly, typeof(string))) { Console.WriteLine(method); } } static IEnumerableGetExtensionMethods(Assembly assembly, Type extendedType) { var query = from type in assembly.GetTypes() where type.IsSealed && !type.IsGenericType && !type.IsNested from method in type.GetMethods(BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic) where method.IsDefined(typeof(ExtensionAttribute), false) where method.GetParameters()[0].ParameterType == extendedType select method; return query; } }
根据John Skeet的回答,我创建了自己的System.Type类型扩展.
using System; using System.Collections.Generic; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; namespace System { public static class TypeExtension { ////// This Methode extends the System.Type-type to get all extended methods. It searches hereby in all assemblies which are known by the current AppDomain. /// ////// Insired by Jon Skeet from his answer on http://stackoverflow.com/questions/299515/c-sharp-reflection-to-identify-extension-methods /// ///returns MethodInfo[] with the extended Method public static MethodInfo[] GetExtensionMethods(this Type t) { ListAssTypes = new List (); foreach (Assembly item in AppDomain.CurrentDomain.GetAssemblies()) { AssTypes.AddRange(item.GetTypes()); } var query = from type in AssTypes where type.IsSealed && !type.IsGenericType && !type.IsNested from method in type.GetMethods(BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic) where method.IsDefined(typeof(ExtensionAttribute), false) where method.GetParameters()[0].ParameterType == t select method; return query.ToArray (); } /// /// Extends the System.Type-type to search for a given extended MethodeName. /// /// Name of the Methode ///the found Methode or null public static MethodInfo GetExtensionMethod(this Type t, string MethodeName) { var mi = from methode in t.GetExtensionMethods() where methode.Name == MethodeName select methode; if (mi.Count() <= 0) return null; else return mi.First (); } } }
它获取当前AppDomain的所有程序集并搜索扩展方法.
用法:
Type t = typeof(Type); MethodInfo[] extendedMethods = t.GetExtensionMethods(); MethodInfo extendedMethodInfo = t.GetExtensionMethod("GetExtensionMethods");
下一步是使用方法扩展System.Type,返回所有方法(也是带有扩展方法的"普通"方法)
这将返回以某种类型定义的所有扩展方法的列表,包括通用方法:
public static IEnumerable> GetExtensionMethodsDefinedInType(this Type t) { if (!t.IsSealed || t.IsGenericType || t.IsNested) return Enumerable.Empty >(); var methods = t.GetMethods(BindingFlags.Public | BindingFlags.Static) .Where(m => m.IsDefined(typeof(ExtensionAttribute), false)); List > pairs = new List >(); foreach (var m in methods) { var parameters = m.GetParameters(); if (parameters.Length > 0) { if (parameters[0].ParameterType.IsGenericParameter) { if (m.ContainsGenericParameters) { var genericParameters = m.GetGenericArguments(); Type genericParam = genericParameters[parameters[0].ParameterType.GenericParameterPosition]; foreach (var constraint in genericParam.GetGenericParameterConstraints()) pairs.Add(new KeyValuePair (parameters[0].ParameterType, m)); } } else pairs.Add(new KeyValuePair (parameters[0].ParameterType, m)); } } return pairs; }
唯一的问题是:返回的Type与typeof(..)所期望的不同,因为它是通用参数类型。为了找到给定类型的所有扩展方法,您必须比较所有基本类型的GUID和Type的接口,例如:
public ListGetExtensionMethodsOf(Type t) { List methods = new List (); Type cur = t; while (cur != null) { TypeInfo tInfo; if (typeInfo.TryGetValue(cur.GUID, out tInfo)) methods.AddRange(tInfo.ExtensionMethods); foreach (var iface in cur.GetInterfaces()) { if (typeInfo.TryGetValue(iface.GUID, out tInfo)) methods.AddRange(tInfo.ExtensionMethods); } cur = cur.BaseType; } return methods; }
要完成:
我保留了一个类型信息对象的字典,该对象是在迭代所有程序集的所有类型时构建的:
private DictionarytypeInfo = new Dictionary ();
其中TypeInfo
定义为:
public class TypeInfo { public TypeInfo() { ExtensionMethods = new List(); } public List Constructors { get; set; } public List Fields { get; set; } public List Properties { get; set; } public List Methods { get; set; } public List ExtensionMethods { get; set; } }