리플렉션 (C #)을 사용하여 메서드가 재정의되었는지 감지
가상 메서드 TestMe ()를 정의하는 기본 클래스 TestBase가 있다고 가정합니다.
class TestBase
{
public virtual bool TestMe() { }
}
이제이 클래스를 상속합니다.
class Test1 : TestBase
{
public override bool TestMe() {}
}
이제 Reflection을 사용하여 TestMe 메서드가 자식 클래스에서 재정의되었는지 확인해야합니다. 가능합니까?
필요한 것-상속의 전체 계층 구조를 표시하고 어떤 수준에서 어떤 가상 메서드가 재정의되었는지 표시하기 위해 "객체"유형에 대한 디자이너 시각화 도우미를 작성하고 있습니다.
유형이 주어지면 Test1
자체
구현
선언 이 있는지 확인할 수 있습니다
TestMe
.
typeof(Test1).GetMethod("TestMe").DeclaringType == typeof(Test1)
선언이 기본 유형에서 나온 경우 false로 평가됩니다.
이것은 실제 구현이 아닌 테스트 선언이므로 자체 선언이 있으므로 추상이고 추상 이면 true 를 반환합니다 . 해당 사례를 제외하려면 다음을 추가하십시오.Test1
TestMe
Test1
&& !GetMethod("TestMe").IsAbstract
@CiprianBortos가 지적했듯이 허용되는 답변은 완전하지 않으며 그대로 사용하면 코드에 불쾌한 버그가 발생합니다.
그의 의견은 마법의 해결책을 제공 GetBaseDefinition()
하지만 DeclaringType
범용 IsOverride
검사 를 원하는지 확인할 필요가 없습니다 (이 질문의 요점이라고 생각합니다) methodInfo.GetBaseDefinition() != methodInfo
.
또는에 대한 확장 메서드로 제공되면 MethodInfo
이것이 트릭을 할 것이라고 생각합니다.
public static class MethodInfoUtil
{
public static bool IsOverride(this MethodInfo methodInfo)
{
return (methodInfo.GetBaseDefinition() != methodInfo);
}
}
Ken Beckett가 제안한 솔루션 을 작동 시킬 수 없었습니다 . 내가 정한 것은 다음과 같습니다.
public static bool IsOverride(MethodInfo m) {
return m.GetBaseDefinition().DeclaringType != m.DeclaringType;
}
요점에 테스트가 있습니다 .
보호 된 멤버 및 속성에 대해서도 작동하는 간단한 솔루션은 다음과 같습니다.
var isDerived = typeof(Test1 ).GetMember("TestMe",
BindingFlags.NonPublic
| BindingFlags.Instance
| BindingFlags.DeclaredOnly).Length == 0;
이것은 여기 에 내 대답 을 다시 게시 한 것이며 차례로이 질문에 대한 참조를 만들었습니다.
사소하지 않은 경우에도 작동하는 방법 :
public bool Overrides(MethodInfo baseMethod, Type type)
{
if(baseMethod==null)
throw new ArgumentNullException("baseMethod");
if(type==null)
throw new ArgumentNullException("type");
if(!type.IsSubclassOf(baseMethod.ReflectedType))
throw new ArgumentException(string.Format("Type must be subtype of {0}",baseMethod.DeclaringType));
while(type!=baseMethod.ReflectedType)
{
var methods=type.GetMethods(BindingFlags.Instance|
BindingFlags.DeclaredOnly|
BindingFlags.Public|
BindingFlags.NonPublic);
if(methods.Any(m=>m.GetBaseDefinition()==baseMethod))
return true;
type=type.BaseType;
}
return false;
}
그리고 몇 가지 추악한 테스트 :
public bool OverridesObjectEquals(Type type)
{
var baseMethod=typeof(object).GetMethod("Equals", new Type[]{typeof(object)});
return Overrides(baseMethod,type);
}
void Main()
{
(OverridesObjectEquals(typeof(List<int>))==false).Dump();
(OverridesObjectEquals(typeof(string))==true).Dump();
(OverridesObjectEquals(typeof(Hider))==false).Dump();
(OverridesObjectEquals(typeof(HiderOverrider))==false).Dump();
(OverridesObjectEquals(typeof(Overrider))==true).Dump();
(OverridesObjectEquals(typeof(OverriderHider))==true).Dump();
(OverridesObjectEquals(typeof(OverriderNothing))==true).Dump();
}
class Hider
{
public virtual new bool Equals(object o)
{
throw new NotSupportedException();
}
}
class HiderOverrider:Hider
{
public override bool Equals(object o)
{
throw new NotSupportedException();
}
}
class Overrider
{
public override bool Equals(object o)
{
throw new NotSupportedException();
}
}
class OverriderHider:Overrider
{
public new bool Equals(object o)
{
throw new NotSupportedException();
}
}
class OverriderNothing:Overrider
{
}
이 답변 에 따르면 MethodAttributes.NewSlot
속성에 대한 테스트를 사용하여 정확한 파생 또는 기본 유형을 알지 못해도 가상 메서드가 재정의되었는지 확인하는 간단한 방법이 있습니다 .
public static bool HasOverride(this MethodInfo method)
{
return (method.Attributes & MethodAttributes.Virtual) != 0 &&
(method.Attributes & MethodAttributes.NewSlot) == 0;
}
다른 확장 방법과 함께
private const BindingFlags Flags = BindingFlags.NonPublic |
BindingFlags.Public | BindingFlags.Instance;
public static bool HasOverride(this Type type, string name, params Type[] argTypes)
{
MethodInfo method = type.GetMethod(name, Flags, null, CallingConventions.HasThis,
argTypes, new ParameterModifier[0]);
return method != null && method.HasOverride();
}
그런 다음 간단히 전화 할 수 있습니다.
bool hasOverride = GetType().HasOverride(nameof(MyMethod), typeof(Param1Type),
typeof(Param2Type), ...);
MyMethod
파생 클래스에서 재정의 되었는지 확인합니다 .
내가 이것을 테스트하는 한, 그것은 잘 작동하는 것 같았습니다 (내 컴퓨터 ™에서).
public static bool HasOverridingMethod(this Type type, MethodInfo baseMethod) {
return type.GetOverridingMethod( baseMethod ) != null;
}
public static MethodInfo GetOverridingMethod(this Type type, MethodInfo baseMethod) {
var flags = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.InvokeMethod;
return type.GetMethods( flags ).FirstOrDefault( i => baseMethod.IsBaseMethodOf( i ) );
}
private static bool IsBaseMethodOf(this MethodInfo baseMethod, MethodInfo method) {
return baseMethod.DeclaringType != method.DeclaringType && baseMethod == method.GetBaseDefinition();
}
이를 수행하는 더 좋고 안전하며 빠른 방법이 있습니다. 이 기술은 클래스 인스턴스의 수명이 길고 IsOverridden 검사를 여러 번 수행해야하는 경우에 적합합니다.
이 문제를 해결하기 위해 리플렉션보다 훨씬 빠른 캐시와 C # 델리게이트를 사용할 수 있습니다!
// Author: Salvatore Previti - 2011.
/// <summary>We need a delegate type to our method to make this technique works.</summary>
delegate int MyMethodDelegate(string parameter);
/// <summary>An enum used to mark cache status for IsOverridden.</summary>
enum OverriddenCacheStatus
{
Unknown,
NotOverridden,
Overridden
}
public class MyClassBase
{
/// <summary>Cache for IsMyMethodOverridden.</summary>
private volatile OverriddenCacheStatus pMyMethodOverridden;
public MyClassBase()
{
// Look mom, no overhead in the constructor!
}
/// <summary>
/// Returns true if method MyMethod is overridden; False if not.
/// We have an overhead the first time this function is called, but the
/// overhead is a lot less than using reflection alone. After the first time
/// this function is called, the operation is really fast! Yeah!
/// This technique works better if IsMyMethodOverridden() should
/// be called several times on the same object.
/// </summary>
public bool IsMyMethodOverridden()
{
OverriddenCacheStatus v = this.pMyMethodOverridden;
switch (v)
{
case OverriddenCacheStatus.NotOverridden:
return false; // Value is cached! Faaast!
case OverriddenCacheStatus.Overridden:
return true; // Value is cached! Faaast!
}
// We must rebuild cache.
// We use a delegate: also if this operation allocates a temporary object
// it is a lot faster than using reflection!
// Due to "limitations" in C# compiler, we need the type of the delegate!
MyMethodDelegate md = this.MyMethod;
if (md.Method.DeclaringType == typeof(MyClassBase))
{
this.pMyMethodOverridden = OverriddenCacheStatus.NotOverridden;
return false;
}
this.pMyMethodOverridden = OverriddenCacheStatus.Overridden;
return true;
}
/// <summary>Our overridable method. Can be any kind of visibility.</summary>
protected virtual int MyMethod(string parameter)
{
// Default implementation
return 1980;
}
/// <summary>Demo function that calls our method and print some stuff.</summary>
public void DemoMethod()
{
Console.WriteLine(this.GetType().Name + " result:" + this.MyMethod("x") + " overridden:" + this.IsMyMethodOverridden());
}
}
public class ClassSecond :
MyClassBase
{
}
public class COverridden :
MyClassBase
{
protected override int MyMethod(string parameter)
{
return 2011;
}
}
class Program
{
static void Main(string[] args)
{
MyClassBase a = new MyClassBase();
a.DemoMethod();
a = new ClassSecond();
a.DemoMethod();
a = new COverridden();
a.DemoMethod();
Console.ReadLine();
}
}
이 프로그램을 콘솔 응용 프로그램으로 실행하면 다음과 같이 인쇄됩니다.
MyClassBase result:1980 overridden:False
ClassSecond result:1980 overridden:False
COverridden result:2011 overridden:True
Visual Studio 2010, C # 4.0으로 테스트되었습니다. 이전 버전에서도 작동해야하지만 새 릴리스에서 대리자에 대한 최적화로 인해 3.0 미만의 C #에서 약간 느릴 수 있습니다. 이에 대한 테스트는 감사하겠습니다. :) 그러나 리플렉션을 사용하는 것보다 여전히 빠릅니다!
참조 URL : https://stackoverflow.com/questions/2932421/detect-if-a-method-was-overridden-using-reflection-c
'IT TIP' 카테고리의 다른 글
정적 메서드는 스레드로부터 안전합니까? (0) | 2020.12.29 |
---|---|
fopen에서 r과 rb의 차이점은 무엇입니까 (0) | 2020.12.29 |
스레드가 자체적으로 교착 상태가 될 수 있습니까? (0) | 2020.12.29 |
Android에서 전역 예외 처리 사용 (0) | 2020.12.29 |
std :: transform () 및 toupper (), 일치하는 함수 없음 (0) | 2020.12.29 |