为什么 CanRead 和 CanWrite 在 C# 中为具有重写访问器的属性返回 false?

Iva*_*mko 5 .net c# reflection system.reflection

当尝试从派生属性获取属性访问器或使用 CanRead / CanWrite 时,出于某种原因,不考虑基本自动属性。

CanReadCanWrite仅基于派生类型返回值,GetMethod以及SetMethod没有包含来自基类型的方法。

但是,当从基类型编写代码访问器时,可以使用(这样我们可以仅使用派生类型中定义的 setter 读取覆盖的自动属性)。

这是作为单元测试编写的重现它的代码:

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);
    }
}
Run Code Online (Sandbox Code Playgroud)

我希望最后两个测试通过,我错过了什么?

Pet*_*iho 4

我预计最后两项测试会通过,我错过了什么?

要获得明确的答案,您必须询问最初设计 .NET 及其类型系统的人。那是说……

在我看来,这与反射提供有关如何编写类型的信息的目标是一致的。考虑另一种选择:如果PropertyInfo返回的对象同时包含派生类的 setter 和基类的 getter,会怎样?从返回的结果中理解实际声明的内容会更加困难,并且PropertyInfo对象本身可能会不一致。这是因为存在一个PropertyInfo.DeclaringType属性,该属性意味着该成员的所有信息仅与该声明类型有关。

对于既不是属性也不是事件的成员(两者都封装了一类成员),您将获得预期的行为。当然,除非您传递BindingFlags.DeclaredOnly,这会将返回的信息限制为声明类型。但对于这些类型的成员,该DeclaringType属性明确地告诉您该成员实际声明的类型。

对于属性,它会告诉您该属性DeclaringType是在哪个类中声明的。然后和属性告诉您该类声明了什么。SetMethodGetMethod

恕我直言,这使得反射 API 更简单、更一致、更容易理解。这确实意味着您必须做更多的工作来分析虚拟属性。但是,反思总是需要“多做一点工作”。:)