奇怪的效果与覆盖属性和反射
本文关键字:覆盖 属性 反射 | 更新日期: 2023-09-27 18:14:58
我在.NET/Reflection中遇到了一个奇怪的行为,无法找到任何解决方案/解释:
class A
{
public virtual string TestString { get; set; }
}
class B : A
{
public override string TestString
{
get { return "x"; }
}
}
由于属性只是函数对(get_PropName()
, set_PropName()
)只覆盖"get"部分,因此应该保留"set"部分,因为它在基类中。如果你试图实例化类B并给TestString
赋值,它就会使用类a的实现。
但是如果我在反射中查看类B的实例化对象会发生什么呢?
PropertyInfo propInfo = b.GetType().GetProperty("TestString");
propInfo.CanRead ---> true
propInfo.CanWrite ---> false(!)
如果我试图从反射中调用setter:
propInfo.SetValue("test", b, null);
我甚至会得到一个ArgumentException
与以下消息:
没有找到属性设置方法。
是否符合预期?因为我似乎没有找到BindingFlags
的GetProperty()
方法的组合,该方法从反射中返回具有工作get/set对的属性。
编辑:如果我在GetProperties()
上使用BindingFlags.DeclaredOnly
,我会期望这种行为,但默认(BindingFlags.Default
)将继承成员考虑在内,并且TestString的setter显然是继承的!
这是一个解决方法:
typeof(B).GetProperty("TestString")
.GetAccessors() // { B.get_TestString() }
.First() // B.get_TestString()
.GetBaseDefinition() // A.get_TestString()
.DeclaringType // typeof(A)
.GetProperty("TestString") // A.TestString: CanRead and CanWrite
这种方法应该是相当健壮的。如果您正在寻找非公共访问器,则需要更加小心this (BindingFlags)。
编辑:注意,这种方法不同于"硬编码"typeof(A).GetProperty("TestString")
或typeof(B).BaseType.GetProperty("TestString")
,因为它找到了声明有问题的属性的实际的原始类型。由于派生类型不可能(至少在c#中不可能)为重写的属性添加新访问器,因此该"原始"类型的属性声明应该包含所有相关的访问器。
你不是在重写一个方法,你是在重写一个属性定义
属性的默认定义包括Get
/Set
方法,而你的新定义只包括Get
方法,所以你的覆盖属性只有Get
可用,而不是Set
是有意义的
编辑
如果你在上面运行类似Reflector的命令,你会看到
class A
{
public virtual string TestString { get; set; }
}
class B : A
{
public override string TestString
{
get { return "x"; }
}
}
编译成类似于
的内容internal class A
{
// Fields
[CompilerGenerated]
private string <TestString>k__BackingField;
// Methods
public A();
// Properties
public virtual string TestString { [CompilerGenerated] get; [CompilerGenerated] set; }
}
internal class B : A
{
// Methods
public B();
// Properties
public override string TestString { get; }
}
当你在代码中设置值时,你实际上是在调用B.base.set_TestValue
之类的东西。当你反射一些东西时,你是在试图找到B.set_TestValue
,它不存在。
虽然不能覆盖属性,但可以覆盖属性定义(前提是它不与基本属性定义冲突)。由于您的问题最初是用WPF标记的,因此我当时正在考虑DependencyProperties,这实际上是属性定义,而不是您可能想到的意义上的属性。