当试图从派生的属性获取属性访问器或使用CanRead / CanWrite时,出于某些原因,未考虑基本的自动属性。
CanRead
和CanWrite
仅基于派生类型返回值,而且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);
}
}
我希望最后两项测试能够通过,我缺少什么?
答案 0 :(得分:4)
我希望最后两项测试能够通过,我缺少什么?
要确定的答案,您必须问起最初设计.NET及其类型系统的人员。那就是……
在我看来,这与提供有关如何编写类型的信息的反射目标是一致的。考虑另一种选择:如果返回的PropertyInfo
对象同时包含派生类的setter和基类的getter。从返回的结果中了解实际上在何处声明的内容将变得更加困难,并且PropertyInfo
对象本身可能会不一致。这是因为存在PropertyInfo.DeclaringType
属性,该属性表示该成员的所有信息仅与该声明类型有关。
使用既不是属性也不是事件的成员(它们都封装了类成员的 pair ),您将获得预期的行为。当然,除非您传递BindingFlags.DeclaredOnly
,否则它将返回的信息限制为声明类型。但是对于这些类型的成员,DeclaringType
属性可以明确地告诉您成员实际声明的类型。
DeclaringType
带有属性,可告诉您属性在哪个类中声明。然后SetMethod
和GetMethod
属性告诉您该类声明了什么。
恕我直言,这使反射API更简单,更一致且更易于理解。这确实意味着您需要做更多的工作来分析虚拟属性。但是,思考总是会涉及“更多的工作”。 :)
答案 1 :(得分:2)
正如Peter Duniho在回答中解释的那样,这似乎需要做一些工作。
如果PropertyInfo
具有类似GetBaseDefinition()
,but it does not(还有this thread)的内容,那么会更容易,因此我们必须使用accessor方法。如果访问器的方法信息具有对属性信息but it does not的引用,也将更加容易,因此我们遍历 all 属性并假定存在完全匹配的内容。>
这是一个幼稚的解决方案:
// does not necessarily work as expected if the property or one of its accessors
// (getter or setter) is not public
internal static bool CanReadExt(PropertyInfo pi)
{
if (pi.CanRead)
return true;
// assume we have a setter since we do not have a getter
var setter = pi.SetMethod
?? throw new Exception("Neither getter nor setter in property?");
// try to acquire setter of base property
var baseSetter = setter.GetBaseDefinition();
// if the property was not overridden, we can return
if (setter.DeclaringType == baseSetter.DeclaringType)
return false;
// try to find the base property
var basePi = baseSetter.DeclaringType.GetProperties()
.SingleOrDefault(x => x.SetMethod == baseSetter)
?? throw new Exception("Set accessor was overridden but we could not find property info for base property.");
// recursively call ourselves
return CanReadExt(basePi);
}
它与您的true
返回PropertiesReflectionTests.Property
,因此在这种情况下可以使用。我想,应对每种情况都需要加倍小心。
如果愿意,可以将此方法作为扩展方法。
可以编写类似的方法CanWriteExt
。