heltonbiker
heltonbiker

Reputation: 27615

Read non-static property value of a subclass via Reflection

I am implementing a variation of the Command design pattern. I have an abstract CodedCommand class with some subclasses, each of one with an overriden "Code" property of type char. Each subclass has then the responsibility to "encapsulate" its own Code letter to which it is associated.

The use case is: when some serial parser receive a char/byte, I want to create an instance of the appropriate subclass, to further process the command payload.

My problem is: I don't know how to get the specific value from each subclass.

My current implementation is below. The problem, highlighted with a comment, is that type.GetProperty("Code") is returning null!

internal abstract class CodedCommand
{
    internal abstract char Code { get; }
}

internal class EventA : CodedCommand
{
    internal override char Code => 'A';
}

internal class EventB : CodedCommand
{
    internal override char Code => 'B';
}

public class CommandCreator
{
    Dictionary<char, Type> _typeMap 
        = new Dictionary<char, Type>();

    public CommandCreator()
    {
        var types = GetType().Assembly.GetTypes()
                                     .Where(type => type.IsSubclassOf(typeof(CodedCommand)))
                                     .Where(type => !type.IsAbstract);

        foreach (var type in types)
        {
            var field = type.GetProperty("Code");  // returns null!!!
            var value = field.GetValue(null);
            var code = (char)value;

            _typeMap[code] = type;
        }
    }


    CodedCommand BuildCommand(char code)
    {
        _typeMap.TryGetValue(code, out Type type);

        if (type != null)
        {
            return (CodedCommand)(Activator.CreateInstance(type));
        }

        return null;
    }
}

So my question is how can I fix this design?

Upvotes: 3

Views: 182

Answers (2)

Ehsan Sajjad
Ehsan Sajjad

Reputation: 62498

The problem is your property is marked as internal, not public and we cannot access the non-public properties using the overload which you are using.

You need to specify the flags parmeter by using this overload if you want properties other than public like:

type.GetProperty("Code",BindingFlags.Instance | 
                        BindingFlags.NonPublic |
                        BindingFlags.Public)

GetProperty(String) overload:

Searches for the public property with the specified name.

GetProperty(string name,BindingFlags bindingAttr) overload:

Searches for the specified property, using the specified binding constraints.

Upvotes: 1

Risto M
Risto M

Reputation: 3009

Here is complete solution for getting value of property:

var types = GetType().Assembly.GetTypes()
   .Where(type => type.IsSubclassOf(typeof(CodedCommand)) && !type.IsAbstract);

foreach (var type in types)
{
    var obj = Activator.CreateInstance(type);
    var field = type.GetProperty("Code", BindingFlags.Instance | BindingFlags.NonPublic);
    var value = field.GetValue(obj);
    var code = (char)value;

    _typeMap[code] = type;
}

Please note that field.GetValue needs object instance to work and NonPublic-BindingFlag is needed when getting PropertyInformation for internal property.

Upvotes: 3

Related Questions