C# 具有覆盖属性和反射的奇怪效果
我在.NET/Reflection中遇到了一个奇怪的行为,无法找到任何解决方案/解释:C# 具有覆盖属性和反射的奇怪效果,c#,.net,reflection,properties,overriding,C#,.net,Reflection,Properties,Overriding,我在.NET/Reflection中遇到了一个奇怪的行为,无法找到任何解决方案/解释: class A { public virtual string TestString { get; set; } } class B : A { public override string TestString { get { return "x"; } } } 由于属性只是一对函数(get_-PropName(),set_-PropName()),因此仅重写“ge
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
:
找不到属性集方法
这和预期的一样吗?因为我似乎找不到GetProperty()
方法的BindingFlags
组合,该方法通过反射返回具有工作get/set对的属性
编辑:
如果我在
GetProperties()
上使用BindingFlags.DeclaredOnly
,我会期望这种行为,但是默认(BindingFlags.default
)会考虑继承的成员,并且TestString的setter显然是继承的 您不是在覆盖方法,而是在覆盖属性定义
属性的默认定义包括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; }
}
内部A类
{
//田地
[编译生成]
私有字符串k__BackingField;
//方法
公共A();
//性质
公共虚拟字符串TestString{[CompilerGenerated]get;[CompilerGenerated]set;}
}
内部B类:A
{
//方法
公共B();
//性质
公共重写字符串TestString{get;}
}
在代码中设置值时,实际上调用了类似B.base.set\u TestValue
的东西。当您反映某个内容时,您试图查找不存在的B.set\u TestValue
虽然不能覆盖特性,但可以覆盖特性定义(前提是它与基本特性定义不冲突)。因为您的问题最初是用WPF标记的,所以我当时考虑的是DependencyProperties,它实际上是属性定义,而不是您可能想到的属性。这里有一个解决方法:
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
这种方法应该相当稳健。如果您正在寻找非公共访问器,则需要更加小心地使用这个(BindingFlags)
编辑:
请注意,这种方法不同于“硬编码”
typeof(A).GetProperty(“TestString”)
或typeof(B).BaseType.GetProperty(“TestString”)
,因为它找到了声明相关属性的实际原始类型。由于派生类型不可能(至少在C中不可能)向重写的属性添加新的访问器,因此此“原始”类型上的属性声明应包含所有相关的访问器。首先,您错过了类a或属性上的“virtual”。然后您重写了一个属性,该属性现在在B中不再具有setter。我发现绑定标志不允许对只读属性进行设置是非常直观的。@Marino:我认为OP的要点是您可以合法地执行var B=new B();b、 TestString=“foo”但是反射将告诉您B.TestString
没有setter。在这方面,GetProperty
的行为似乎与GetMethod
等不同@LukeH我无法做到var b=new b();b、 TestString=“foo”代码>。它告诉我(正确地)无法分配属性TestString,因为它是只读的。我可以做ab=newb();b、 TestString=“foo”代码>,但这是因为我将B存储为A,A可以设置TestString
属性。但是它实际上没有做任何事情,因为在获取值时,它读的是B.get_TestString()
,而不是A.get_TestString()
@LukeH我复制/粘贴了原始问题中的类定义,除了我将virtual
添加到TestString
属性定义中,因为否则它不会编译。是的,我确信它不会编译:)我正在使用.NET4.0、VS2010和Windows7(如果有必要的话)。在LinqPad中为我编译。但是,正如预期的那样,如果我删除覆盖
,而不是覆盖属性,它就不会编译。我不太理解这一点,这不是说typeof(a).GetProperty(“TestString”)
或typeof(B).BaseType.GetProperty(“TestString”)的一种迂回方式吗
?@H.B.:我试图通过编辑来解决您的问题。这没有帮助,因为您无法调用propInfo.SetValue(“test”,B,null);-<它会抱怨传递了错误类型的对象。propinfo需要一个类型为A的对象。也许你可以通过强制转换来实现,但我还没有尝试过。这是一个不错的方法,但我不是真的在寻找解决方法,而是想解释一下.NET反射的这种行为是否真的正确。属性只是运行时的方法,应该遵循应用于方法的继承规则——这就是它们所做的,反射除外。我没有向该属性添加新定义,也没有删除它们,我只是覆盖GETTER(get_TestString())并选择不覆盖SETTER(set_TestString())。代码就是这样,但反射告诉我这个属性不再有setter了。这是错误的。对不起,你的解释对我来说没有多大意义。您不能覆盖