Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/.net/20.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 具有覆盖属性和反射的奇怪效果_C#_.net_Reflection_Properties_Overriding - Fatal编程技术网

C# 具有覆盖属性和反射的奇怪效果

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

我在.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

找不到属性集方法

这和预期的一样吗?因为我似乎找不到
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了。这是错误的。对不起,你的解释对我来说没有多大意义。您不能覆盖