Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why is generic type not the correct type?

Tags:

c#

generics

I have two classes, BaseClass and Person. The Person class inherits from BaseClass. I then use the following generic method.

    public static PropertyInfo GetProperty<T>(T item, Func<PropertyInfo, bool> predicate)
    {
        return GetProperty(typeof(T), predicate);
    }

Within the BaseClass I have a method, that invokes GetProperty

public class BaseClass
{
    public void DoSomething()
    {
        var property = GetProperty(new Person(), (property) => p.Name == "Name");
    }
}

I then call this method from a unit test.

var person = new Person();
person.DoSomething();

When typeof(T) is used, BaseClass is returned. If I use item.GetType() then Person is returned. If I inspect them within the debugger, T is of type BaseClass and item is of type Person. Why does it not infer that T is a typeof(Person), when it already knows that item is a Person?

Edit

My example was wrong above sorry, when I call GetProperty, I pass this.

public class BaseClass
{
    public void DoSomething()
    {
        var property = GetProperty(this, (property) => p.Name == "Name");
    }
}
like image 873
Johnathon Sullinger Avatar asked Nov 01 '22 14:11

Johnathon Sullinger


1 Answers

The reason for this inference problem is that generic inference happens at compile time, and is thus identical to specifying the desired type by hand. If you switch to explicitly stating <Person> with the call, does it throw an error during compile?

One way around this is to make sure that the variable (note: NOT the Object!) you pass in is explicitly of the Person class, as it seems to be in your code. Another way is to force generic inference to occur at runtime by using a dyanmic object thusly:

GetProperty(this as dynamic, (prop) => prop.Name == "Name");

By casting this as dynamic it calls GetProperty<dynamic> using the exact type of this at runtime. The problem with this approach is that dynamics are extremely slow compared to other objects.

If your models are strictly a single level of inheritance, you can also utilize Static Polymorphism to handle the generic parameter. Thusly:

public class BaseClass<TSelf> where TSelf : BaseClass<TSelf>

public sealed class Model : BaseClass<Model>

This way you could use TSelf as the parameter in place of T and it would be exactly correct. the problem with this approach is it strictly limits you to a flat single-inheritance hierarchy, as anything inheriting from Model would go back to the original problem and be seen as a Model because it can't override the generic parameter used by its base class.

Assuming your GetProperty function does some reflection to check for a property, you may want to consider passing a Type object in, instead of using a generic and then typeof(T), as .GetType() is far more accurate.

like image 193
David Avatar answered Nov 15 '22 05:11

David