使用动态类型调用通用方法

72
假设我有以下类:
public class Animal { .... }

public class Duck : Animal { ... }

public class Cow : Animal { ... }

public class Creator
{
   public List<T> CreateAnimals<T>(int numAnimals)
   {
      Type type = typeof(T);
      List<T> returnList = new List<T>();
      //Use reflection to populate list and return
   }
}

现在,在一些稍后的代码中,我想读取要创建的动物是什么。
Creator creator = new Creator();
string animalType = //read from a file what animal (duck, cow) to create
Type type = Type.GetType(animalType);
List<animalType> animals = creator.CreateAnimals<type>(5);

现在的问题是最后一行不合法。有没有更优雅的方法来解决这个问题呢?

3
在这里使用泛型并不是最好的选择,你应该创建一个List<Animal>,然后使用Activator创建派生类。 - Doggett
5个回答

89

我不知道是否优雅,但做法是:

typeof(Creator)
    .GetMethod("CreateAnimals")
    .MakeGenericMethod(type)
    .Invoke(creator, new object[] { 5 });

5
除非您已经在编译时知道类型,否则您将无法将其强制转换为 List<Duck>List<Cow> 等。您能做的最好的事情就是转换为 IList - LukeH

35

不完全是这样。你需要使用反射。泛型主要是针对静态类型而非在执行时才知道的类型。

要使用反射,你需要使用 Type.GetMethod 来获取该方法的定义,然后调用 MethodInfo.MakeGenericMethod(type),最后像调用其他方法那样调用它。


5

试试这个:

public List<T> CreateAnimals<T>(int numAnimals) where T : Animal
{
    Type type = typeof(T);
    List<T> returnList = new List<T>();
    //Use reflection to populate list and return
}

它应该确保CreateAnimals允许的类型是从Animal继承而来的。希望这样一来,就不会出现问题:List<animalType> animals = creator.CreateAnimals<type>(5);


3
如果发帖者想要从字符串中读取要创建的动物类型,那么这并没有真正帮到他。 - Jon Skeet

2
这个要点在于MakeGenericType()和MakeGenericMethod()。一旦你使用动态类型,就不能回到静态类型了。你可以通过使用Activator.CreateInstance(typeof(List<>).MakeGenericType(type))来动态创建列表,并使用类似的反射方法动态调用泛型方法。

0
List<animalType> animals = 
 creator.CreateAnimals<type>(5);

在你的例子中,animalTypetype是运行时变量,而不是类型,所以这当然是无意义的。只有在编译时知道类型的情况下,通用版本才有意义,例如:
List<Animal> animals = 
  creator.CreateAnimals<Cow>(5);

如果你必须相应地限制类型,那么你就不得不进行强制类型转换。如果类型未知,则必须完全依赖反射...


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接