使用Reflection(C#)检测方法是否被覆盖

时间:2010-05-28 20:47:28

标签: c# reflection override

假设我有一个基类TestBase,我定义了一个虚拟方法TestMe()

class TestBase
{
    public virtual bool TestMe() {  }
}

现在我继承了这个类:

class Test1 : TestBase
{
    public override bool TestMe() {}
}

现在,使用Reflection,我需要查找TestMe方法是否已在子类中重写 - 是否可能?

我需要它 - 我正在为类型“object”编写一个设计器可视化器来显示整个继承层次结构,并显示在哪个级别覆盖了哪些虚拟方法。

8 个答案:

答案 0 :(得分:62)

给定类型Test1,您可以确定它是否有TestMe实现声明:

typeof(Test1).GetMethod("TestMe").DeclaringType == typeof(Test1)

如果声明来自基本类型,则会评估为false。

请注意,由于这是测试声明,而不是真正的实现,如果Test1也是抽象的并且TestMe是抽象的,那么返回true,因为Test1会有自己的声明。如果要排除该情况,请添加&& !GetMethod("TestMe").IsAbstract

答案 1 :(得分:21)

正如@CiprianBortos所指出的那样,接受的答案并不完整,如果按原样使用,会导致代码中出现令人讨厌的错误。

他的评论提供了神奇的解决方案GetBaseDefinition(),但如果您想要进行通用DeclaringType检查,则无需检查IsOverride(我认为这是此问题的重点) ),只是methodInfo.GetBaseDefinition() != methodInfo

或者,作为MethodInfo上的扩展方法提供,我认为这样可以解决问题:

public static class MethodInfoUtil
{
    public static bool IsOverride(this MethodInfo methodInfo)
    {
        return (methodInfo.GetBaseDefinition() != methodInfo);
    }
}

答案 2 :(得分:18)

我无法让Ken Beckett's proposed solution工作。这就是我所确定的:

    public static bool IsOverride(MethodInfo m) {
        return m.GetBaseDefinition().DeclaringType != m.DeclaringType;
    }

the gist中有测试。

答案 3 :(得分:6)

一个简单的解决方案也适用于受保护的成员和属性如下:

var isDerived = typeof(Test1 ).GetMember("TestMe", 
               BindingFlags.NonPublic 
             | BindingFlags.Instance 
             | BindingFlags.DeclaredOnly).Length == 0;

这是我的回复here的转贴,后者反过来引用了这个问题。

答案 4 :(得分:2)

一种在某些非常重要的情况下也适用的方法:

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
{

}

答案 5 :(得分:2)

根据this answer,还可以通过一种简单的方法来检查虚拟方法是否被覆盖,而不必使用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

据我测试过,它似乎工作正常(在我的机器上)。

答案 6 :(得分:1)

where customer_date < curdate() + interval (1 - day(curdate()) day and
      customer_date >= (curdate() + interval (1 - day(curdate()) day) - interval 1 year)

答案 7 :(得分:0)

有一种更好,更安全,更快捷的方法。 如果您的类实例具有较长的使用寿命并且必须多次执行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可能会慢一些,对此的测试将不胜感激:) 然而,它仍然比使用反射更快!