Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
340 views
in Technique[技术] by (71.8m points)

c# - How does reflection tell me when a property is hiding an inherited member with the 'new' keyword?

So if I have:

public class ChildClass : BaseClass
{
    public new virtual string TempProperty { get; set; }
}

public class BaseClass
{
    public virtual string TempProperty { get; set; }
}

How can I use reflection to see that ChildClass is hiding the Base implementation of TempProperty?

I'd like the answer to be agnostic between c# and vb.net

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

We'll have to deal in terms of the methods of the property here rather than the property itself, because it is the get/set methods of the property that actually get overridden rather than the property itself. I'll use the get method as you should never have a property without one, though a complete solution should check for the lack of one.

Looking at the IL emitted in a number of cases, the 'get' method of the base property will have the metadata tokens (this is from the C# compiler; others may not emit the hidebysig depending on their method hiding semantics, in which case the method would be hide-by-name):

non-virtual : .method public hidebysig specialname instance
virtual     : .method public hidebysig specialname newslot virtual instance 

The derived one will have the following tokens:

override    : .method public hidebysig specialname virtual instance 
new         : .method public hidebysig specialname instance
new virtual : .method public hidebysig specialname newslot virtual instance 

So we can see from this that it isn't possible to tell purely from the method's metadata tokens whether it is new because the non-virtual base method has the same tokens as the non-virtual new method, and the virtual base method has the same tokens as the new virtual method.

What we can say is that if the method has the virtual token but not the newslot token then it overrides a base method rather than shadows it, i.e.

var prop = typeof(ChildClass).GetProperty("TempProperty");
var getMethod = prop.GetGetMethod();
if ((getMethod.Attributes & MethodAttributes.Virtual) != 0 &&
    (getMethod.Attributes & MethodAttributes.NewSlot) == 0)
{
    // the property's 'get' method is an override
}

Assuming, then, that we find the 'get' method is not an override, we want to know whether there is a property in the base class that it is shadowing. The problem is that because the method is in a different method table slot, it doesn't actually have any direct relationship to the method it is shadowing. So what we're actually saying is "does the base type have any method which meets the criteria for shadowing", which varies depending on whether the method is hidebysig or hide-by-name.

For the former we need to check whether the base class has any method which matches the signature exactly, whereas for the latter we need to check whether it has any method with the same name, so continuing the code from above:

else 
{
    if (getMethod.IsHideBySig)
    {
        var flags = getMethod.IsPublic ? BindingFlags.Public : BindingFlags.NonPublic;
        flags |= getMethod.IsStatic ? BindingFlags.Static : BindingFlags.Instance;
        var paramTypes = getMethod.GetParameters().Select(p => p.ParameterType).ToArray();
        if (getMethod.DeclaringType.BaseType.GetMethod(getMethod.Name, flags, null, paramTypes, null) != null)
        {
            // the property's 'get' method shadows by signature
        }
    }
    else
    {
        var flags = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.Instance;
        if (getMethod.DeclaringType.BaseType.GetMethods(flags).Any(m => m.Name == getMethod.Name))
        {
            // the property's 'get' method shadows by name
        }
    }
}

I think this is most of the way there, but I still don't think it's exactly right. For a start I'm not totally familiar with hiding by name as C# doesn't support it and that's pretty much all I use, so I may be wrong in the code here that indicates an instance method could shadow a static one. I also don't know about the case sensitivity issue (e.g. in VB could a method called Foo shadow a method called foo if they both had the same signature and were both hidebysig - in C# the answer is no but if the answer is yes in VB then it means the answer to this question is actually nondeterministic).

Well, I'm not sure how much help all this is, other than to illustrate that it's actually a much harder problem than I thought it would be (or I've missed something really obvious in which case I'd like to know!). But hopefully it's got sufficient content that it helps you achieve what you're trying to do.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...