C# reflection GetValue from a field in generic base class
Asked Answered
R

2

6

the problem is that we cannot GetValue of a field (non generic) that only resides in base class that has generic type. please see the code snippet below. calling

f.GetValue(a)

will throw the exception with message: Late bound operations cannot be performed on fields with types for which Type.ContainsGenericParameters is true.

class Program
{
    static void Main(string[] args)
    {
        Type abstractGenericType = typeof (ClassB<>);
        FieldInfo[] fieldInfos =
            abstractGenericType.GetFields(BindingFlags.Public |  BindingFlags.Instance);

        ClassA a = new ClassA("hello");
        foreach(FieldInfo f in fieldInfos)
        {
            f.GetValue(a);// throws InvalidOperationhException 
        }
    }
}

internal class ClassB<T>
{
    public string str;
    public ClassB(string s)
    {
        str = s;
    }
}

internal class ClassA : ClassB<String>
{
    public ClassA(string value) : base(value)
    {}
}

Our design requires that we obtain FieldInfo first before we have any instances of actual objects. so we cannot use

Type typeA = abstractGenericType.MakeGenericType(typeof(string));
FieldInfo[] fieldInfos = typeA.GetFields();

Thank you

Rupture answered 11/7, 2012 at 23:2 Comment(1)
The question as posted doesn't make much sense. You will have to use the object instance to call GetValue(). So simply use a.GetType() to get the concrete generic type, no point in going through the incomplete type.Analyzer
G
0

I guess the problem comes from the point, that generic classes are dynamically compiled with the specific type. a generic type could also be defined like

internal class ClassB<T>
{
    public T value;

    public string str;
    public ClassB(string s)
    {
        str = s;
    }
}

then you would have a problem getting the the value of field "value". workarounds would be using retrieving the type directly by using .GetType(), or creating a new base class without generic arguments containing the fields you want to access.

Grummet answered 12/7, 2012 at 0:15 Comment(0)
G
0

If you are flexible and willing to use properties instead of fields, you can accomplish something like what you want by putting a non-generic interface on the generic base class. Here is a re-factored version of your original code showing the change and the concept.

    class Program
    {
        public static void Main(string[] args)
        {
            Type interfaceType = typeof(IGetStr);
            PropertyInfo[] propertyInfos = interfaceType.GetProperties(BindingFlags.Public | BindingFlags.Instance);

            ClassA a = new ClassA("hello");

            foreach (PropertyInfo p in propertyInfos)
            {
                var myValue = p.GetValue(a, null); // does not throw InvalidOperationException 
            }
        }
    }

    internal interface IGetStr
    {
        string StringValue { get; set; }
    }

    internal class ClassB<T> : IGetStr
    {
        public string str;

        public ClassB(string s)
        {
            str = s;
        }

        public string StringValue
        {
            get
            {
                return str;
            }
            set
            {
                str = value;
            }
        }
    }

    internal class ClassA : ClassB<String>
    {
        public ClassA(string value)
            : base(value)
        { }
    }

Interfaces are a great way to get access to the non-generic properties of generic classes. Using an interface as shown, you can get at the property called "StringValue" for any class that inherits from ClassB<> regardless of the generic type, as long as you use the interface of IGetStr.

You could also cast your "ClassA" object to IGetStr and the following will work. (Great for lists of items that all inherit from your generic base)

var result = ((IGetStr)a).StringValue;
Gunmaker answered 12/7, 2013 at 18:53 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.