14

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

ekolis
  • 6,270
  • 12
  • 50
  • 101
  • 1
    I don't understand how this could work for private methods of non-abstract classes. Can you please provide an SSCCE? http://sscce.org/ – Daniel Dec 24 '13 at 16:15
  • The true parameter to GetSetMethod is supposed to search for non-public members. – ekolis Dec 24 '13 at 16:17
  • 1
    Odd, I'm fairly certain `GetSetMethod(true)` has always resolved private `set` accessors for me, but I don't believe I've used it to resolve members on a base class. Perhaps there is an inconsistency in the behavior. Also, are you sure your `PropertyInfo` refers to the correct property, and not a property on a subclass that hides the one you are looking for? – Mike Strobel Dec 24 '13 at 16:18
  • Nope, don't have any overriding/hiding properties... – ekolis Dec 24 '13 at 16:40

6 Answers6

10

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.

TyCobb
  • 8,909
  • 1
  • 33
  • 53
  • Yes, I am calling it from a derived class. That makes sense, that the property setter doesn't exist... – ekolis Dec 24 '13 at 16:33
8

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.

PiotrWolkowski
  • 8,408
  • 6
  • 48
  • 68
  • 4
    Thank you, works great. In my case, I used `someType.GetProperty(name).DeclaringType.GetProperty(name).GetSetMethod(true)` to retrieve the method. This is shorter and works because the property is public, and it's just the setter which is private. I was just missing the `DeclaringType`. – fero Aug 05 '16 at 11:53
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.

Mike Strobel
  • 25,075
  • 57
  • 69
1

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);
Mike Dinescu
  • 54,171
  • 16
  • 118
  • 151
  • This doesn't seem to work - I still need to set the property setter to protected. Could the fact that I'm using GetProperties to enumerate over all properties have something to do with this? – ekolis Dec 24 '13 at 16:27
  • @ekolis You have to also use `BindingFlags.FlattenHierarchy` because your property is in a base class. – TyCobb Dec 24 '13 at 16:28
  • `BindingFlags.FlattenHierarchy` only applies to static members. – Mike Strobel Dec 24 '13 at 16:31
  • This doesn't solve his problem. He can resolve the property; he cannot resolve the `set` accessor. The problem lies in the type being reflected upon: it needs to be the property's declaring type. – Mike Strobel Dec 24 '13 at 16:33
  • @MikeStrobel No, it doesn't. It also forces the inclusion of inherited members. – TyCobb Dec 24 '13 at 16:34
  • I tried FlattenHierarchy, and that didn't seem to help... I have in my watch window: – ekolis Dec 24 '13 at 16:35
  • s.GetType().GetProperties(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.FlattenHierarchy).ToArray()[29].GetSetMethod(true) -- which is null (29 being the index of the property in the list of properties which I was looking at in the debugger). – ekolis Dec 24 '13 at 16:35
  • @TyCobb: Inherited members are included by default; there is no switch to include them, but they can be omitted with `DeclaredOnly`. `FlattenHierarchy` provides a means of including static members because they are not technically "inherited" (at least according to a somewhat arbitrary definition of 'inherited'). – Mike Strobel Dec 24 '13 at 16:38
  • @MikeStrobel Got it. I will try to remember that next time. I could of sworn I had to use Flatten before with inheritance, but that could have been easily due to it being static and I not realizing it at the time. Thanks for the info. – TyCobb Dec 24 '13 at 16:44
1

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!

HappyNomad
  • 4,458
  • 4
  • 36
  • 55
0

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

Adam Straughan
  • 2,766
  • 3
  • 20
  • 27