Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why do CanRead and CanWrite return false in C# for properties with overridden accessors?

When trying to get properties accessors from derived properties or use CanRead / CanWrite, for some reason base auto-properties are not taken into account.

CanRead and CanWrite return values based only on the derived type, also GetMethod and SetMethod don't contain methods from base type.

However when writing code accessors from base type can be used (so that we can read overridden auto-property with only setter defined in derived type).

Here is the code to reproduce it written as an unit test:

using System.Reflection;
using NUnit.Framework;

[TestFixture]
public class PropertiesReflectionTests
{
    public class WithAutoProperty
    {
        public virtual object Property { get; set; }
    }

    public class OverridesOnlySetter : WithAutoProperty
    {
        public override object Property
        {
            set => base.Property = value;
        }
    }

    private static readonly PropertyInfo Property = typeof(OverridesOnlySetter).GetProperty(nameof(OverridesOnlySetter.Property));

    // This one is passing
    [Test]
    public void Property_ShouldBeReadable()
    {
        var overridesOnlySetter = new OverridesOnlySetter {Property = "test"};

        Assert.AreEqual(overridesOnlySetter.Property, "test");
    }

    // This one is failing
    [Test]
    public void CanRead_ShouldBeTrue()
    {
        Assert.True(Property.CanRead);
    }

    // And this is failing too
    [Test]
    public void GetMethod_ShouldBeNotNull()
    {
        Assert.NotNull(Property.GetMethod);
    }
}

I expected last two tests to pass, what am I missing?

like image 890
Ivan Shimko Avatar asked Sep 02 '19 20:09

Ivan Shimko


Video Answer


1 Answers

I expected last two tests to pass, what am I missing?

For a definitive answer, you'd have to ask the people who originally designed .NET and its type system. That said…

It seems to me that this is consistent with the goal of reflection providing information about how a type was written. Consider the alternative: what if the PropertyInfo object returned included both the setter from the derived class and the getter from the base class. It would be considerably more difficult to understand from the returned result what was actually declared where, and the PropertyInfo object itself would arguably be inconsistent. This is because there is the PropertyInfo.DeclaringType property which implies that all of the information for the member pertains just to that declaring type.

With members which are neither properties nor events (both of which encapsulate a pair of class members), you get the behavior you expected. Unless of course you pass BindingFlags.DeclaredOnly, which restricts the returned information to the declaring type. But in the case of those types of members, the DeclaringType property tells you unequivocally in which type the member was actually declared.

With a property, the DeclaringType tells you in which class the property was declared. And then the SetMethod and GetMethod properties tell you what that class declared.

IMHO, this makes the reflection API simpler, more consistent, and easier to understand. It does mean that you have to do a little more work to analyze virtual properties. But then, reflection is always going to involve "a little more work". :)

like image 58
Peter Duniho Avatar answered Oct 03 '22 21:10

Peter Duniho