Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Reflection and generic types

I'm writing some code for a class constructor which loops through all the properties of the class and calls a generic static method which populates my class with data from an external API. So I've got this as an example class:

public class MyClass{
  public string Property1 { get; set; }
  public int Property2 { get; set; }
  public bool Property3 { get; set; }

  public static T DoStuff<T>(string name){
    // get the data for the property from the external API
    // or if there's a problem return 'default(T)'
  }
}

Now in my constructor I want something like this:

public MyClass(){
  var properties = this.GetType().GetProperties();
  foreach(PropertyInfo p in properties){
    p.SetValue(this, DoStuff(p.Name), new object[0]);
  }
}

So the above constructor will thrown an error because I'm not supplying the generic type.

So how do I pass in the type of the property in?

like image 869
Aaron Powell Avatar asked Oct 13 '08 07:10

Aaron Powell


People also ask

What is generic type?

A generic type is a generic class or interface that is parameterized over types. The following Box class will be modified to demonstrate the concept.

Do generics use reflection?

Because the Common Language Runtime (CLR) has access to generic type information at run time, you can use reflection to obtain information about generic types in the same way as for non-generic types.

How do I use reflection to call a generic method?

The first step to dynamically invoking a generic method with reflection is to use reflection to get access to the MethodInfo of the generic method. To do that simply do this: var methodInfo = typeof(ClassWithGenericMethod). GetMethod("MethodName");

What is generic class with example?

A Generic class simply means that the items or functions in that class can be generalized with the parameter(example T) to specify that we can add any type as a parameter in place of T like Integer, Character, String, Double or any other user-defined type.


2 Answers

Do you want to call DoStuff<T> with T = the type of each property? In which case, "as is" you would need to use reflection and MakeGenericMethod - i.e.

var properties = this.GetType().GetProperties();
foreach (PropertyInfo p in properties)
{
    object value = typeof(MyClass)
    .GetMethod("DoStuff")
    .MakeGenericMethod(p.PropertyType)
    .Invoke(null, new object[] { p.Name });
    p.SetValue(this, value, null);
}

However, this isn't very pretty. In reality I wonder if it wouldn't be better just to have:

static object DoStuff(string name, Type propertyType);
... and then
object value = DoStuff(p.Name, p.PropertyType);

What does the generics give you in this example? Note that value-types will still get boxed etc during the reflection call - and even then boxing isn't as bad as you might think.

Finally, in many scenarios, TypeDescriptor.GetProperties() is more appropriate than Type.GetProperties() - allows for flexible object models etc.

like image 100
Marc Gravell Avatar answered Oct 11 '22 04:10

Marc Gravell


Was your constructor code meant to read like this:

public MyClass(){
  var properties = this.GetType().GetProperties();
  foreach(PropertyInfo p in properties){
    p.SetValue(this, DoStuff(p.Name), new object[0]);
  }
}

? Note the DoStuff instead of MyClass.

If so, the problem is that you're trying to use generics when they're really not applicable. The point of generics (well, one of the points) is to use compile-time type safety. Here you don't know the type at compile time! You could call the method by reflection (fetching the open form and then calling MakeGenericMethod) but that's pretty ugly.

Does DoStuff really need to be generic in the first place? Is it being used from elsewhere? The parameter to PropertyInfo.SetValue is just object, so you'd still get boxing etc even if you could call the method generically.

like image 22
Jon Skeet Avatar answered Oct 11 '22 03:10

Jon Skeet