I've come across a strange behaviour in .NET/Reflection and cannot find any solution/explanation for this:
class A
{
public virtual string TestString { get; set; }
}
class B : A
{
public override string TestString
{
get { return "x"; }
}
}
Since properties are just pairs of functions (get_PropName()
, set_PropName()
) overriding only the "get" part should leave the "set" part as it is in the base class. And this is just what happens if you try to instanciate class B and assign a value to TestString
, it uses the implementation of class A.
But what happens if I look at the instantiated object of class B in reflection is this:
PropertyInfo propInfo = b.GetType().GetProperty("TestString");
propInfo.CanRead ---> true
propInfo.CanWrite ---> false(!)
And if I try to invoke the setter from reflection with:
propInfo.SetValue("test", b, null);
I'll even get an ArgumentException
with the following message:
Property set method not found.
Is this as expected? Because I don't seem to find a combination of BindingFlags
for the GetProperty()
method that returns me the property with a working get/set pair from reflection.
EDIT:
I would expect that behaviour if I'd use BindingFlags.DeclaredOnly
on GetProperties()
but the default (BindingFlags.Default
) takes inherited members into account and the setter of TestString clearly is inherited!
Here's a workaround:
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 approach should be reasonably robust. You will need to be more careful with this (BindingFlags) if you're looking for non-public accessor(s).
EDIT:
Note that this approach is different from "hardcoding" typeof(A).GetProperty("TestString")
or typeof(B).BaseType.GetProperty("TestString")
because it finds the actual, original type that declares the property in question. Since it isn't possible (not in C# at least) for a derived type to add new accessors to an overridden property, the property-declaration on this "original" type should contain all the relevant accessors.
You're not overwritting a method, you're overwritting a property definition
The default definition of the property includes Get
/Set
methods, and your new definition only includes a Get
method, so it makes sense that your overwritten property only has Get
available, not Set
Edit
If you run something like Reflector on this, you'll see that
class A
{
public virtual string TestString { get; set; }
}
class B : A
{
public override string TestString
{
get { return "x"; }
}
}
compiles into something like that looks like
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; }
}
When you set the value in code, you are actually calling something like B.base.set_TestValue
. When you reflect something, you are trying to find B.set_TestValue
, which doesn't exist.
While true that you cannot overwrite a property, you can overwrite a property definition (providing it doesn't conflict with the base property definition). Since your question was originally tagged with WPF, I was thinking of DependencyProperties at the time, which are actually property definitions, and not properties in the sense that you might be thinking of.