C# 识别扩展方法的反射

C# 识别扩展方法的反射,c#,reflection,extension-methods,C#,Reflection,Extension Methods,在C#中,是否有一种技术使用反射来确定一个方法是否已作为扩展方法添加到类中 给定如下所示的扩展方法,是否可以确定已将Reverse()添加到string类中 public static class StringExtensions { public static string Reverse(this string value) { char[] cArray = value.ToCharArray(); Array.Reverse(cArray)

在C#中,是否有一种技术使用反射来确定一个方法是否已作为扩展方法添加到类中

给定如下所示的扩展方法,是否可以确定已将Reverse()添加到string类中

public static class StringExtensions
{
    public static string Reverse(this string value)
    {
        char[] cArray = value.ToCharArray();
        Array.Reverse(cArray);
        return new string(cArray);
    }
}

我们正在寻找一种机制来确定单元测试中的扩展方法是由开发人员适当添加的。尝试这样做的一个原因是,开发人员可能会将类似的方法添加到实际的类中,如果是,编译器将选择该方法。

您必须查找可能定义扩展方法的所有程序集

查找用修饰的类,然后查找该类中也用
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 IEnumerable<MethodInfo> GetExtensionMethods(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;
    }
}
使用系统;
使用System.Runtime.CompilerServices;
运用系统反思;
使用System.Linq;
使用System.Collections.Generic;
公共静态类扩展
{
公共静态void Foo(此字符串x){}
公共静态无效条(字符串x){}//不是ext.方法
公共静态void Baz(此int x){}//不在字符串上
}
公共静态类扩展
{
公共静态void qux(此字符串x){}
}
公开课考试
{
静态void Main()
{
组装此组装=类型(测试)。组装;
foreach(GetExtensionMethods中的MethodInfo方法)(此程序集,
类型(字符串)))
{
控制台写入线(方法);
}
}
静态IEnumerable GetExtensionMethods(程序集,
类型(扩展类型)
{
var query=来自程序集中的类型。GetTypes()
其中type.IsSealed&!type.IsGenericType&&!type.IsNested
从type.GetMethods(BindingFlags.Static)中的方法
|BindingFlags.Public | BindingFlags.NonPublic)
其中method.IsDefined(typeof(ExtensionAttribute),false)
其中方法.GetParameters()[0]。ParameterType==extendedType
选择方法;
返回查询;
}
}

您必须查看所有可能定义扩展方法的程序集

查找用修饰的类,然后查找该类中也用
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 IEnumerable<MethodInfo> GetExtensionMethods(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;
    }
}
使用系统;
使用System.Runtime.CompilerServices;
运用系统反思;
使用System.Linq;
使用System.Collections.Generic;
公共静态类扩展
{
公共静态void Foo(此字符串x){}
公共静态无效条(字符串x){}//不是ext.方法
公共静态void Baz(此int x){}//不在字符串上
}
公共静态类扩展
{
公共静态void qux(此字符串x){}
}
公开课考试
{
静态void Main()
{
组装此组装=类型(测试)。组装;
foreach(GetExtensionMethods中的MethodInfo方法)(此程序集,
类型(字符串)))
{
控制台写入线(方法);
}
}
静态IEnumerable GetExtensionMethods(程序集,
类型(扩展类型)
{
var query=来自程序集中的类型。GetTypes()
其中type.IsSealed&!type.IsGenericType&&!type.IsNested
从type.GetMethods(BindingFlags.Static)中的方法
|BindingFlags.Public | BindingFlags.NonPublic)
其中method.IsDefined(typeof(ExtensionAttribute),false)
其中方法.GetParameters()[0]。ParameterType==extendedType
选择方法;
返回查询;
}
}

为了澄清Jon掩盖的一点。。。向类“添加”扩展方法不会以任何方式更改类。这只是C#编译器执行的一点点旋转

所以,用你的例子,你可以写

string rev = myStr.Reverse();
但是写入程序集的MSIL将与您编写的程序集完全相同:

string rev = StringExtensions.Reverse(myStr);

编译器只是让你自欺欺人,以为你在调用字符串的方法。

为了澄清Jon掩盖的一点。。。向类“添加”扩展方法不会以任何方式更改类。这只是C#编译器执行的一点点旋转

所以,用你的例子,你可以写

string rev = myStr.Reverse();
但是写入程序集的MSIL将与您编写的程序集完全相同:

string rev = StringExtensions.Reverse(myStr);
编译器只是让你自欺欺人地认为你在调用字符串的方法

尝试这样做的一个原因是,开发人员可能会将类似的方法添加到实际类中,如果是,编译器将选择该方法

  • 假设定义了一个扩展方法void Foo(this Customer someCustomer)
  • 另外,假设修改了Customer并添加了void Foo()方法
  • 然后,Customer上的新方法将覆盖/隐藏扩展方法
此时调用旧Foo方法的唯一方法是:

CustomerExtension.Foo(myCustomer);
尝试这样做的一个原因是,开发人员可能会将类似的方法添加到实际类中,如果是,编译器将选择该方法

  • 假设定义了一个扩展方法void Foo(this Customer someCustomer)
  • 另外,假设修改了Customer并添加了void Foo()方法
  • 那么,t
    public class TypeInfo
    {
        public TypeInfo()
        {
            ExtensionMethods = new List<MethodInfo>();
        }
    
        public List<ConstructorInfo> Constructors { get; set; }
    
        public List<FieldInfo> Fields { get; set; }
        public List<PropertyInfo> Properties { get; set; }
        public List<MethodInfo> Methods { get; set; }
    
        public List<MethodInfo> ExtensionMethods { get; set; }
    }
    
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Reflection;
    using System.Runtime.CompilerServices;
    
    namespace System
    {
        public static class TypeExtension
        {
            /// <summary>
            /// 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.
            /// </summary>
            /// <remarks>
            /// Insired by Jon Skeet from his answer on http://stackoverflow.com/questions/299515/c-sharp-reflection-to-identify-extension-methods
            /// </remarks>
            /// <returns>returns MethodInfo[] with the extended Method</returns>
    
            public static MethodInfo[] GetExtensionMethods(this Type t)
            {
                List<Type> AssTypes = new List<Type>();
    
                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<MethodInfo>();
            }
    
            /// <summary>
            /// Extends the System.Type-type to search for a given extended MethodeName.
            /// </summary>
            /// <param name="MethodeName">Name of the Methode</param>
            /// <returns>the found Methode or null</returns>
            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<MethodInfo>() <= 0)
                    return null;
                else
                    return mi.First<MethodInfo>();
            }
        }
    }
    
    Type t = typeof(Type);
    MethodInfo[] extendedMethods = t.GetExtensionMethods();
    MethodInfo extendedMethodInfo = t.GetExtensionMethod("GetExtensionMethods");
    
    void Main()
    {
        var test = new Test();
        var testWithMethod = new TestWithExtensionMethod();
        Tools.IsExtensionMethodCall(() => test.Method()).Dump();
        Tools.IsExtensionMethodCall(() => testWithMethod.Method()).Dump();
    }
    
    public class Test 
    {
        public void Method() { }
    }
    
    public class TestWithExtensionMethod
    {
    }
    
    public static class Extensions
    {
        public static void Method(this TestWithExtensionMethod test) { }
    }
    
    public static class Tools
    {
        public static MethodInfo GetCalledMethodInfo(Expression<Action> expr)
        {
            var methodCall = expr.Body as MethodCallExpression;
            return methodCall.Method;
        }
    
        public static bool IsExtensionMethodCall(Expression<Action> expr)
        {
            var methodInfo = GetCalledMethodInfo(expr);
            return methodInfo.IsStatic;
        }
    }