简体   繁体   中英

How can I use reflection to pass each list of “MyTypes” to a generic method with a constraint of T:MyType?

How can I use reflection to pass each list of "MyTypes" to a generic method with a constraint of T:MyDataObject?

public interface IAllMyTypes
{
    List<FirstType> MyType1 { get; set; }
    List<SecondType> MyType2 { get; set; }
    List<ThirdType> MyType3 { get; set; }
}

FirstType, SecondType, and ThirdType inherit from MyDataObject (as demonstrated below), but have different properties.

public class FirstType : MyDataObject
{
  //various properties
}   

I've been unable to pass the data into a method with this signature:

void DoSomething<T>(IEnumerable<T> enumerable) where T : MyDataObject;

The error is that "type arguments can not be inferred."

Here is my unsuccessful attempt:

public void DoSomethingWithAllMyTypes(IAllMyTypes allMyTypes)
{
    foreach (PropertyInfo propertyInfo in allMyTypes.GetType().GetProperties())
    {
        var x = propertyInfo.GetValue(allMyTypes) as IList;//im not sure what to do here
        if(x==null) throw new Exception("still wrong");

        DoSomething(x);
    }
}   

All of the code in DoSomething(..) works correctly if I provide the properties directly which looks like:

public void DoSomethingWithAllMyTypes(IAllMyTypes allMyTypes)
{
    DoSomething(allMyTypes.MyType1);
    DoSomething(allMyTypes.MyType2);
    DoSomething(allMyTypes.MyType3);
}

If you want to use reflection, you can invoke your helper method using reflection, too:

You will have to obtain a MethodInfo to a generic method and create a generic method reflection handle to actually Invoke the method. The type T of the generic method needs to be obtained at runtime in that case.

public void DoSomethingWithAllMyTypes(IAllMyTypes allMyTypes)
{
    var method = this.GetType().GetMethod("DoSomething", BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Public);
    foreach (PropertyInfo propertyInfo in allMyTypes.GetType().GetProperties())
    {
        var x = propertyInfo.GetValue(allMyTypes, null);
        if(x==null) throw new Exception("still wrong");

        // obtain the type from the property - other techniques can be used here.
        var genericMethod = method.MakeGenericMethod(new[] {propertyInfo.PropertyType.GetGenericArguments()[0]})
        //execute the generic helper
        genericMethod.Invoke(this, new[]{x});
    }
} 

public void DoSomething<T>(IList<T> list) where T : MyDataObject {

}

I'm struggling to find a case where you'd need to structure your data the way you did without over-complicating things. If you've found a legit case please comment and I'll update my answer to reflect your needs.

You can start with your base class, make it abstract and put an abstract method in it DoSomething

public abstract class MyDataObject{
  public string SomeSharedProperty{get;set;}
  protected abstract DoSomething(); 
}

public class FirstType: MyDataObject{ 
  protected override DoSomething(){
    Console.WriteLine(SomeSharedProperty);
  }
}


public class Consumer{
  public void DoSomethingWithAllMyTypes(List<MyDataObject> source)
  {
    source.ForEach(x=>x.DoSomething());
  }
}

You could use the Linq method call Cast<T> to convert your list to the right type

public void DoSomethingWithAllMyTypes(IAllMyTypes allMyTypes)
{
    foreach (PropertyInfo propertyInfo in allMyTypes.GetType().GetProperties())
    {
        var x = propertyInfo.GetValue(allMyTypes) as IEnumerable
        if(x==null) throw new Exception("still wrong");

        DoSomething(x.Cast<MyDataObject>());
    }
}  

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM