仮想メソッド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と評価されます。
これは真の実装ではなくテスト宣言であるため、_Test1
_も抽象でTestMe
の場合、これはwillがtrueを返すことに注意してください。 _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;
}
the Gist にはテストがあります。
保護されたメンバーとプロパティでも機能する簡単なソリューションは次のとおりです。
var isDerived = typeof(Test1 ).GetMember("TestMe",
BindingFlags.NonPublic
| BindingFlags.Instance
| BindingFlags.DeclaredOnly).Length == 0;
これは私の回答の再投稿 here であり、この質問への言及がありました。
この答え によると、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 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
{
}
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でテスト済み。以前のバージョンでも動作するはずですが、新しいリリースではデリゲートを最適化しているため、C#3.0未満では少し遅くなる可能性があります。これに関するテストはありがたいです:)ただし、リフレクションを使用するよりも高速です!