如何区分重写和隐藏的方法?

8

考虑以下几个变体:

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属性略有不同:

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



更新2:

我在谷歌上搜索关于VtableLayoutMask的内容,回到了StackOverflow

更新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;
    }
}

2
"VtableLayoutMask"是调试器中误导性的信息,它实际上不是属性位的名称。它是一个位掩码,用于测试其他位,其中实际上只有1个位,即第16位为0表示ReuseSlot,为1表示NewSlot。这才是你真正要查找的内容,测试MethodAttributes.NewSlot以保持代码的可读性。 - Hans Passant
3个回答

4

更新:文档似乎暗示IsHideBySig是答案,但实际上并非如此。

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

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

您可以依赖IsHideBySig属性。如果方法具有new修饰符,它将为true
请注意,上述内容仅适用于C#方法。文档详细说明如下:
当派生类中的成员声明使用C#new修饰符或Visual Basic Shadows修饰符时,它会隐藏基类中同名的成员。 C#通过签名隐藏基类成员。也就是说,如果基类成员具有多个重载版本,则隐藏的仅是具有完全相同签名的那个版本。相比之下,Visual Basic隐藏所有基类重载。因此,IsHideBySig在使用Visual Basic Shadows修饰符声明的成员上返回false,并且在使用C#new修饰符声明的成员上返回true

@voroninp,您的意思是即使方法是override而不是new,它也是“true”吗? - Frédéric Hamidi
@voroninp,文档中还提到了NewSlot属性,我已经使用了这种策略更新了我的答案。 - Frédéric Hamidi
1
有趣的是,但反编译源代码显示NewSlot等于VtableLayoutMask。 - Pavel Voronin
1
此外,VtableLayoutMask 的存在是为了如果添加其他与 vtable 布局相关的标志,则可以通过掩码获取所有这些标志。因此,可能会出现将来的框架具有多个使用该掩码查看的标志,并且对于设置了 NewSlot 的方法,(method.Attributes&MethodAttributes.VtableLayoutMask) == MethodAttributes.NewSlot 将返回 false。如果您要采用此方法,则需要使用 (method.Attributes&MethodAttributes.MethodAttributes.NewSlot) == MethodAttributes.NewSlot(method.Attributes&MethodAttributes.MethodAttributes.NewSlot)!= 0 - Jon Hanna
@JonHanna 写道:如果问题中的 public override virtual void Doit() 改为 public override void Doit() ... 在 C# 中,不允许同时使用 overridevirtual 关键字,因此该问题并不提供合法的 C# 代码。但是,override 可以与 sealed 关键字组合或不组合。确定一个方法是否是重写的最好方法是将其 .DeclaringType 与其 .GetBaseDefinition().DeclaringType 进行比较,因为 GetBaseDefinition() 给出了“槽位”中最顶层(非重写)方法的声明,即 virtualabstract 方法声明。 - Jeppe Stig Nielsen
显示剩余4条评论

3

如果使用new隐藏,则DeclaringType将不同。例如,运行以下代码:

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
< p > WillBeInheritted的方法和基本定义的声明类型都是A

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
你可以使用 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 表示 Giraffe 继承的 override(未更改)。而 mi.GetBaseDefinition() 获取另一个 MethodInfo ,该方法代表源代码中没有带有 override 关键字的方法 Animal.M


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接