8

Consider these variants:

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

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

or

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

I cannot find the difference in the returned results of the call typeof(B).GetMethod("Doit");

In both cases MethodInfo.DecalringType is class B and other properties seem the same. Do I miss something or there is no way to distinguish them?



Update:

When I ran the sample in LINQPAd I noticed slight difference in Attributes property:

for new virtual value was - PrivateScope, Public, Virtual, HideBySig, VtableLayoutMask
for override - PrivateScope, Public, Virtual, HideBySig



Update 2:

I googled about VtableLayoutMask and came back to StackOverflow

Udate 3:

resulting code:

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;
    }
}
Community
  • 1
  • 1
Pavel Voronin
  • 13,503
  • 7
  • 71
  • 137
  • 2
    "VtableLayoutMask" is misleading info from the debugger, it is not actually a name of an attribute bit. It is a bit-mask, used to test for other bits. Of which there are actually only 1, bit #16 is 0 to indicate ReuseSlot, it is 1 to indicate NewSlot. Which is what you are really looking for, test for MethodAttributes.NewSlot to keep your code readable. – Hans Passant May 23 '14 at 09:37

3 Answers3

4

Update: The documentation seems to imply that IsHideBySig is the answer, but it does not appear to be the case in practice.

Another strategy is to rely on the presence of the NewSlot attribute:

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

Original, incorrect answer follows.

You can rely on the IsHideBySig property. It will be true if the method has the new modifier.

Note the above only applies to C# methods. The documentation elaborates with:

When a member in a derived class is declared with the C# new modifier or the Visual Basic Shadows modifier, it can hide a member of the same name in the base class. C# hides base class members by signature. That is, if the base class member has multiple overloads, the only one that is hidden is the one that has the identical signature. By contrast, Visual Basic hides all the base class overloads. Thus, IsHideBySig returns false on a member declared with the Visual Basic Shadows modifier, and true on a member declared with the C# new modifier.

Frédéric Hamidi
  • 258,201
  • 41
  • 486
  • 479
  • It is true in both cases as I can see – Pavel Voronin May 23 '14 at 09:15
  • @voroninp, do you mean it is `true` even if the method is `override` and not `new`? – Frédéric Hamidi May 23 '14 at 09:17
  • yes, exacly. See my question, I added an update. It's the only difference I've noticed so far. – Pavel Voronin May 23 '14 at 09:20
  • @voroninp, the documentation also mentions the `NewSlot` attribute, I updated my answer with this strategy. – Frédéric Hamidi May 23 '14 at 09:24
  • 1
    Interesting enough but decompiled source shows that NewSlot equals to VtableLayoutMask – Pavel Voronin May 23 '14 at 09:33
  • @JonHanna Yes, sure, I check for virtual. You answer is also very good. At least there is less magic with strange attributes =) – Pavel Voronin May 23 '14 at 09:52
  • 1
    Also, `VtableLayoutMask` exists so that if other vtable-layout related flags where added, you could get all of them by masking. As such its possible a future framework would have more than one flag seen with that mask and `(method.Attributes & MethodAttributes.VtableLayoutMask) == MethodAttributes.NewSlot` would return false for a method with `NewSlot` set. If you are going to take this approach you want either `(method.Attributes & MethodAttributes.MethodAttributes.NewSlot) == MethodAttributes.NewSlot` or `(method.Attributes & MethodAttributes.MethodAttributes.NewSlot) != 0` – Jon Hanna May 23 '14 at 09:53
  • @JonHanna wrote: _if the question had `public override void Doit()` instead of `public override virtual void Doit()` ..._ Combining `override` and `virtual` keywords is never allowed in C#, so the question does not provide legal C# code. An `override` may or may not be combined with the `sealed` keyword, however. The best way to find out if a method is an override is to compare its `.DeclaringType` to its `.GetBaseDefinition().DeclaringType` because `GetBaseDefinition()` gives the top-most (non-override) method of the "slot". That is the `virtual` or `abstract` method declaration. – Jeppe Stig Nielsen May 23 '14 at 11:03
  • @JeppeStigNielsen sorry, that was a hasty copy-paste, and it's now too late to edit. Correction: `NewSlot` on its own isn't fully informative: It'll be false for an overridden method, or a non-virtual method, including a non-virtual hiding method (if the question had `public new void Doit()` instead of `public new virtual void Doit()`), so if you take this approach you'd also have to check whether `Virtual` was set or not. – Jon Hanna May 23 '14 at 11:07
3

The DeclaringType will be different if it was hidden with new. For example, run:

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();
    }
}

The output will be:

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

WillBeInheritted has A as the declaring type for both the method and the base definition's declaring type.

WillBeOverridden has A for the base definition's declaring type, B for the declaring type.

WillBeHidden has two versions, the hidden one in A and the hiding one in B. This makes sense when we consider:

B b = new B();
A a = b;
b.WillBeHidden(); // calls hiding method.
a.WillBeHidden(); // calls hidden method on same object.
Jon Hanna
  • 110,372
  • 10
  • 146
  • 251
2

You can use GetBaseDefinition to find out where this method was first declared.

For example if you let var mi = typeof(B).GetMethod("Doit"); you can check if mi.GetBaseDefinition() == mi or if mi.GetBaseDefinition().DeclaringType == typeof(B) etc.


Here is an example:

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
  }
}

The MethodInfo instance mi represents the override as inherited (unchanged) to Giraffe. And mi.GetBaseDefinition() fetches another MethodInfo which represents instead the method Animal.M which does not carry the override keyword in the C# source.

Jeppe Stig Nielsen
  • 60,409
  • 11
  • 110
  • 181