확장 방법을 식별하기위한 반영
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 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;
}
}
John Skeet의 답변을 기반으로 System.Type-type에 대한 자체 확장을 만들었습니다.
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>();
}
}
}
현재 AppDomain에서 모든 어셈블리를 가져오고 확장 된 메서드를 검색합니다.
용법:
Type t = typeof(Type);
MethodInfo[] extendedMethods = t.GetExtensionMethods();
MethodInfo extendedMethodInfo = t.GetExtensionMethod("GetExtensionMethods");
다음 단계는 모든 메소드 (확장 된 메소드가있는 "일반"메소드)를 리턴하는 메소드로 System.Type을 확장하는 것입니다.
그러면 일반 형식을 포함하여 특정 형식에 정의 된 모든 확장 메서드 목록이 반환됩니다.
public static IEnumerable<KeyValuePair<Type, MethodInfo>> GetExtensionMethodsDefinedInType(this Type t)
{
if (!t.IsSealed || t.IsGenericType || t.IsNested)
return Enumerable.Empty<KeyValuePair<Type, MethodInfo>>();
var methods = t.GetMethods(BindingFlags.Public | BindingFlags.Static)
.Where(m => m.IsDefined(typeof(ExtensionAttribute), false));
List<KeyValuePair<Type, MethodInfo>> pairs = new List<KeyValuePair<Type, MethodInfo>>();
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<Type, MethodInfo>(parameters[0].ParameterType, m));
}
}
else
pairs.Add(new KeyValuePair<Type, MethodInfo>(parameters[0].ParameterType, m));
}
}
return pairs;
}
여기에는 한 가지 문제가 있습니다. 반환 된 Type은 일반적인 매개 변수 유형이기 때문에 typeof (..)에서 예상했던 것과 동일하지 않습니다. 주어진 유형에 대한 모든 확장 메소드를 찾으려면 다음과 같이 유형의 모든 기본 유형 및 인터페이스의 GUID를 비교해야합니다.
public List<MethodInfo> GetExtensionMethodsOf(Type t)
{
List<MethodInfo> methods = new List<MethodInfo>();
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 Dictionary<Guid, TypeInfo> typeInfo = new Dictionary<Guid, TypeInfo>();
은 여기서 TypeInfo
정의된다 :
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; }
}
Jon이 강조한 요점을 명확히하기 위해 ... 클래스에 확장 메서드를 "추가"한다고해서 클래스가 변경되는 것은 아닙니다. C # 컴파일러가 수행하는 약간의 회전입니다.
따라서 귀하의 예를 사용하여 다음과 같이 작성할 수 있습니다.
string rev = myStr.Reverse();
but the MSIL written to the assembly will be exactly as if you had written it:
string rev = StringExtensions.Reverse(myStr);
The compiler is merely letting you fool yourself into thinking you are calling an method of String.
One reason to attempt this is that it is possible that a similar method would be added to the actual class by the developer and, if it was, the compiler will pick that method up.
- Suppose an extension method void Foo(this Customer someCustomer) is defined.
- Suppose, also, that Customer is modified and the method void Foo() is added.
- Then, the new method on Customer will cover/hide the extension method.
The only way to call the old Foo method at that point is:
CustomerExtension.Foo(myCustomer);
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;
}
}
Outputs:
False
True
참고URL : https://stackoverflow.com/questions/299515/reflection-to-identify-extension-methods
'Programing' 카테고리의 다른 글
자바 : NIO와 NIO.2의 차이점은 정확히 무엇입니까? (0) | 2020.10.27 |
---|---|
오른쪽의 예외에도 불구하고 C ++에서 할당이 발생합니다. (0) | 2020.10.27 |
Asp.Net MVC PDF를 생성하기 위해보기를 얻는 방법 (0) | 2020.10.27 |
시스템에 설치된 애플리케이션 가져 오기 (0) | 2020.10.27 |
JavaScriptSerializer.Deserialize-필드 이름을 변경하는 방법 (0) | 2020.10.27 |