代码之家  ›  专栏  ›  技术社区  ›  Andrey

检测是否使用反射(c)重写了方法

  •  54
  • Andrey  · 技术社区  · 15 年前

    假设我有一个基类testbase,我在其中定义了一个虚拟方法testme()。

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

    现在我继承了这个类:

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

    现在,使用反射,我需要找到方法testme是否在子类中被重写了——这是可能的吗?

    我需要它做什么——我正在为“object”类型编写一个设计器可视化工具,以显示继承的整个层次结构,并显示在哪个级别被重写的虚拟方法。

    7 回复  |  直到 8 年前
        1
  •  60
  •   Rex M    15 年前

    给定类型 Test1 ,您可以确定它是否有自己的 实施 宣言 TestMe :

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

    如果声明来自基类型,则计算结果为false。

    注意,由于这是测试声明,而不是真正的实现,因此 如果返回真 测试1 也是抽象的 泰斯米 是抽象的,因为 测试1 会有自己的声明。如果要排除该案例,请添加 && !GetMethod("TestMe").IsAbstract

        2
  •  20
  •   Pondidum    8 年前

    正如@ciprianbortos所指出的,接受的答案并不完整,如果按原样使用,将导致代码中出现严重的错误。

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

    或者,作为扩展方法提供给 MethodInfo 我认为这会起到作用:

    public static class MethodInfoUtil
    {
        public static bool IsOverride(this MethodInfo methodInfo)
        {
            return (methodInfo.GetBaseDefinition() != methodInfo);
        }
    }
    
        3
  •  16
  •   Community CDub    8 年前

    我无法得到 Ken Beckett's proposed solution 工作。我的决定是:

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

    中有个测试 the gist .

        4
  •  6
  •   Community CDub    8 年前

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

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

    这是我的回答 here ,这反过来也提到了这个问题。

        5
  •  2
  •   CodesInChaos    13 年前

    在一些非平凡的情况下也能工作的方法:

    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
    {
    
    }
    
        6
  •  2
  •   Community CDub    8 年前

    根据 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 在派生类中重写。

    就我所测试的而言,它似乎工作得很好(在我的机器上)。

        7
  •  0
  •   Salvatore Previti    13 年前

    有一种更好、更安全、更快的方法。 如果类实例的使用寿命很长,并且必须多次执行isoverriden检查,则此技术是有意义的。

    为了解决这个问题,我们可以使用缓存和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的版本上,由于对新版本中的代理进行了优化,因此速度可能会慢一些,对此的测试将不胜感激:) 但是,它仍然比使用反射更快!