有可能分辨出被覆盖和隐藏的方法吗?

时间:2014-05-23 09:07:21

标签: c# .net reflection

考虑以下变体:

class A
{
    public virtual void Doit()
    {
    }
}

class B : A
{
    public new virtual void Doit()
    {
    }
}

class B : A
{
    public override virtual void Doit()
    {
    }
}

我找不到调用typeof(B).GetMethod("Doit");

的返回结果的差异

在这两种情况下,MethodInfo.DecalringType都是B类,其他属性看起来是一样的。 我错过了什么或者没有办法区分它们吗?



更新

当我在LINQPAd中运行示例时,我注意到Attributes属性中的细微差别:

for new virtual值为 - PrivateScope,Public,Virtual,HideBySig, VtableLayoutMask
for override - PrivateScope,Public,Virtual,HideBySig



更新2:

我用Google搜索VtableLayoutMask并回到StackOverflow

Udate 3:

结果代码:

public static class MethodInfoExtensions
{
    public static bool IsOverriden(this MethodInfo method)
    {
        Contract.Requires<ArgumentNullException>(method != null, "method");

        return method.IsVirtual
               && !method.IsStatic
               // overriden exactly in this class
               && method.ReflectedType == method.DeclaringType
               // not new and not declared for the first time in the class 
               && method.GetBaseDefinition() != method;
    }
}

3 个答案:

答案 0 :(得分:4)

更新:文档似乎暗示IsHideBySig就是答案,但在实践中似乎并非如此。

另一种策略是依赖于NewSlot属性的存在:

public static bool HasNewModifier(this MethodInfo method)
{
     return (method.Attributes & MethodAttributes.VtableLayoutMask)
         == MethodAttributes.NewSlot;
}

  

原始的,错误的答案如下。

您可以依赖IsHideBySig属性。如果方法具有true修饰符,则为new

请注意,上述内容仅适用于 C#方法。文档详细说明:

  

使用C#new修饰符声明派生类中的成员时   或Visual Basic Shadows修饰符,它可以隐藏相同的成员   基类中的名称。 C#通过签名隐藏基类成员。那   是,如果基类成员有多个重载,那么唯一的一个   隐藏的是具有相同签名的那个。相比之下,   Visual Basic隐藏所有基类重载。因此,IsHideBySig   对使用Visual Basic false声明的成员返回Shadows   修饰符,以及使用C#true修饰符声明的成员上的new

答案 1 :(得分:3)

DeclaringType如果隐藏new,则会有所不同。例如,运行:

public class A
{
    public virtual void WillBeInheritted()
    {

    }
    public virtual void WillBeOverridden()
    {

    }
    public virtual void WillBeHidden()
    {

    }
}
public class B : A
{
    public override void WillBeOverridden()
    {

    }
    public virtual new void WillBeHidden()
    {

    }
}
class Program
{
    public static void Main(string[] args)
    {
        foreach(var meth in typeof(B).GetMethods())
        {
            Console.Write(meth.Name);
            Console.Write(": ");
            Console.Write(meth.GetBaseDefinition().DeclaringType.Name);
            Console.Write(" ");
            Console.WriteLine(meth.DeclaringType.Name);
        }
        Console.Read();
    }
}

输出将是:

WillBeOverridden: A B
WillBeHidden: B B
WillBeInheritted: A A
WillBeHidden: A A
ToString: Object Object
Equals: Object Object
GetHashCode: Object Object
GetType: Object Object

WillBeInherittedA作为方法和基本定义的声明类型的声明类型。

WillBeOverridden对于基本定义的声明类型有A,声明类型为B

WillBeHidden有两个版本,A中隐藏的版本和B中的隐藏版本。当我们考虑时,这是有道理的:

B b = new B();
A a = b;
b.WillBeHidden(); // calls hiding method.
a.WillBeHidden(); // calls hidden method on same object.

答案 2 :(得分:2)

您可以使用 GetBaseDefinition 查找首次声明此方法的位置。

例如,如果您允许var mi = typeof(B).GetMethod("Doit");,则可以检查mi.GetBaseDefinition() == mimi.GetBaseDefinition().DeclaringType == typeof(B)等。


以下是一个例子:

class Animal : object
{
  public virtual void M()
  {
  }
}
class Mammal : Animal
{
  public override void M()
  {
  }
}
class Giraffe : Mammal
{
}
static class Test
{
  internal static void Run()
  {
    var mi = typeof(Giraffe).GetMethod("M");

    Console.WriteLine(mi.ReflectedType);                     // Giraffe
    Console.WriteLine(mi.DeclaringType);                     // Mammal
    Console.WriteLine(mi.GetBaseDefinition().DeclaringType); // Animal
  }
}

MethodInfo实例mi表示override继承(未更改)为Giraffe。而mi.GetBaseDefinition()会抓取另一个MethodInfo代替方法Animal.M,该方法不会在C#源中带有override关键字。