Reflection can't find private setter on proper

2019-06-16 12:45发布

问题:

When I have this property in an abstract class:

public IList<Component> Components { get; private set; }

Then when I call:

p.GetSetMethod(true)

with p being a PropertyInfo object pointing to my property, I get null.

However if I change the property setter to protected, I can see it via reflection. Why is this? I don't seem to recall having this problem with non-abstract classes...

回答1:

I assume you are calling this on an object from a derived type of your abstract class. There isn't a property setter at all on that class. It is only located on your abstract base. This is why it works when you mark it as protected. You need use your abstract class' Type instead when getting the property setter.



回答2:

It's an old thread but I run into similar issue recently and none of the above worked for me. Adding my solution as it may be useful for others.

As said before if the setter of a property is private it does not exist in the inherited class. What worked for me was to go one level lower using DeclaringType of the PropertyInfo

So the code to retrieve the property with the setter would look like this:

var propertyInfo = typeof(MyClass)
    .GetProperty("Components", BindingFlags.NonPublic | BindingFlags.Instance)
    .DeclaringType
    .GetProperty("Components", BindingFlags.NonPublic | BindingFlags.Instance);

In this case the propertyInfo contains a value for SetMethod so you can set the value using reflection.



回答3:

Some brief experimentation in the C# Interactive window suggests that for a property P declared on a class A, the following works just fine:

var p = typeof(A).GetProperty("P").GetSetMethod(true)

But as soon as you attempt the same thing with a subclass of A, GetSetMethod no longer resolves the private set accessor:

// class B : A {}
var p = typeof(B).GetProperty("P").GetSetMethod(true) // produces 'null'.

In other words, what you attempted apparently only works for private accessors when the reflected type is the same as the property's declaring type.



回答4:

The trick is to use the BindingFlags enumeration to specify that you want private members to be included when you obtain the PropertyInfo object:

PropertyInfo p = obj.GetType().GetProperty("Components", BindingFlags.NonPublic | BindingFlags.Instance);


回答5:

To build on the work of @piotrwolkowski

var flags = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance;
var propertyInfo = typeof(MyClass).GetProperty("Components", flags);

// only go to the declaring type if you need to
if (!propertyInfo.CanWrite)
    propertyInfo = propertyInfo.DeclaringType.GetProperty("Components", flags);

I added both public and non-public to the binding flags for my use case (it may be overkill and I don't have the time to pursue it further)

I was setting the instance of an object that inherited from an abstract base with public get and private set

Again, all credit to @piotrwolkowski



回答6:

The following experimentation uncovered the issue for me. Note that the base class doesn't have to be abstract to reproduce the problem.

public class Base
{
    public string Something { get; private set; }
}

public class Derived : Base { }

public class MiscTest
{
    static void Main( string[] args )
    {
        var property1 = typeof( Derived ).GetProperty( "Something" );
        var setter1 = property1.SetMethod; //null
        var property2 = typeof( Base ).GetProperty( "Something" );
        var setter2 = property2.SetMethod; //non-null

        bool test1 = property1 == property2; //false
        bool test2 = property1.DeclaringType == property2.DeclaringType; //true

        var solution = property1.DeclaringType.GetProperty( property1.Name );
        var setter3 = solution.SetMethod; //non-null
        bool test3 = solution == property1; //false
        bool test4 = solution == property2; //true
        bool test5 = setter3 == setter2; //true
    }
}

What I learned from this and found surprising, is that the PropertyInfo on the derived type is a different instance than the PropertyInfo on the base type. Weird!