1

How can I get the type of a property of T in a base class?

The class has a collection of type T called Values:

public class BaseHistoryViewModel<T>

public ObservableCollection<T> Values { get; set; }

This type T is always a class derived from another base class BaseHistoryItemViewModel that has a property Value<T> where T : BaseModel

In my BaseHistoryViewModel I want to determine what the type of the Value property is:

    protected virtual async void HandleOnDataChanged(object sender, DataChangedEventArgs e)
    {
        if (e.DataModelType == typeof(T))
        {
            await this.LoadDataAsync(true);
        }
    }

So far this would resolve to Weight == WeightItemViewModelso I really need something like:

typeof(T.Value). I could implement this method in all my derived classes instead but there are nine of them as would rather handle this in the base if possible. Any help would be greatly appreciated.

UPDATE: This is what I did in the end from @Zohar's answer:

protected virtual async void HandleOnDataChanged(object sender, DataChangedEventArgs e)
{
    var typeOfValues = typeof(T).GetProperty("Value").PropertyType;
    if (e.DataModelType == typeOfValues)
    {
        await this.LoadDataAsync(true);
    }
}
Magic Bullet Dave
  • 9,006
  • 10
  • 51
  • 81
  • I think `T.Values.GetType()` should work. – Zev Spitz Aug 19 '18 at 06:33
  • You shouldn't do that as it would break your encapsulation and prevent you from utilizing polymorphism – Amit Aug 19 '18 at 06:33
  • Not sure I understand @Amit can you explain further please? – Magic Bullet Dave Aug 19 '18 at 06:40
  • 1
    Possible duplicate of [How to get the type of T from a member of a generic class or method?](https://stackoverflow.com/questions/557340/how-to-get-the-type-of-t-from-a-member-of-a-generic-class-or-method) – shA.t Aug 19 '18 at 07:01
  • The fact that your function explicitly compares types (in this case the type of a member of the generic type) means it had to be modified any time a new type is introduced into your system. That's the opposite of the goal of polymorphism and is a kind of implementation leak – Amit Aug 19 '18 at 11:41

1 Answers1

3

This type T is always a class derived from another base class BaseHistoryItemViewModel

In that case, you can use a generic constraint to indicate that:

public class BaseHistoryViewModel<T> where T : BaseHistoryItemViewModel

Once you do that, the compiler will know that every T has a generic property called Value - so you could use typeof(T.Value) (Please note that it's not actually T.Value but the actual reference of type T - suppose you have an instance of T called Item, it would be typeof(Item.Value)) in BaseHistoryViewModel<T> just as you want.

However, Please note that the fact that you are using typeof(T) inside a generic method or class is a pretty good indicator that you are not using generics properly in the first place.
The point of using generics is to be able to write a single method (or class) to act on a variety of different types without special handling for each type.

Update

You can use reflection to get the type of the Values property from inside the BaseHistoryViewModel<T> class, like this:

this.GetType().GetProperty("Values").PropertyType.GetGenericArguments()[0];

This will return the type of T for the Values property - even if it does not contain any elements.

Of course, you can use the same technique inside the BaseHistoryItemViewModel<T> class to get the type of the Value property.

Zohar Peled
  • 79,642
  • 10
  • 69
  • 121
  • Thanks @Zohar. Added the where clause constraint and that is all good. There is something wrong with the syntax of typeof(T.Value), I get an error Cannot do a member lookup on T because it is a type parameter. Any ideas? – Magic Bullet Dave Aug 19 '18 at 06:39
  • 1
    Yes, sorry, It's not `T.Value`, but rather the instance of `T`'s Value property. I thought that was quite obvious. Editing my answer. – Zohar Peled Aug 19 '18 at 06:41
  • The things is, I have this.Values (which are of type T) but it is a collection that may not have any items in it. So I am not sure how I can get the equivalent of typeof(this.Values[0].Value) when Values is empty. – Magic Bullet Dave Aug 19 '18 at 06:49
  • Thanks @Zohar. Made a slight alteration: var typeOfValues = this.GetType().GetProperty("Values").PropertyType.GetGenericArguments()[0].GetProperty("Value").PropertyType; and it works. Are there any null checks I need to make? So now to decide whether I should be doing this at all :) – Magic Bullet Dave Aug 19 '18 at 08:02
  • 1
    No null checks needed, `this` can never be null, and everything else is just types, so they will not be null either. And yes, if you are using generics, you should probably not be messing around with types inside. Perhaps a better alternative would be to create the `LoadDataAsync` as an abstract method in the `BaseModel` and then implement it in it's derived classes, sparing you the need to check what exact type is the `Value` property. – Zohar Peled Aug 19 '18 at 08:07