从C#类型变量初始化泛型变量

本文关键字:泛型 变量 初始化 类型变量 | 更新日期: 2023-09-27 18:21:20

我有一个类,它将泛型类型作为初始化的一部分。

public class AnimalContext<T>
{
    public DoAnimalStuff()
    {
        //AnimalType Specific Code
    }
}

我现在能做的是

AnimalContext<Donkey> donkeyContext = new AnimalContext<Donkey>();
AnimalContext<Orca> orcaContext = new AnimalContext<Orca>();

但我需要/想要做的是能够声明一个AnimalContext,它被初始化为一个只有在运行时才知道的类型。例如,

Animal a = MyFavoriteAnimal(); //returns an instance of a class 
                               //implementing an animal
AnimalContext<a.GetType()> a_Context = new AnimalContext<a.GetType()>();
a_Context.DoAnimalStuff();

这可能吗?我似乎在网上找不到答案。

从C#类型变量初始化泛型变量

这部分的意思是可能:

new AnimalContext<a.GetType()>();

显然,确切的语法是错误的,我们将讨论到这一点,但可以在运行时构造泛型类型的实例,因为在runtime之前您不知道类型参数。

这部分的意思是而不是

AnimalContext<a.GetType()> a_Context

也就是说,如果您不知道编译时的类型参数,则不可能将变量键入为泛型类型。泛型是编译时构造,并且依赖于在编译器中具有可用的类型信息。考虑到这一点,如果在编译时不知道泛型的类型,那么就失去了泛型的所有好处。

现在,当您在运行时才知道泛型类型时,要在运行时构造泛型类型的实例,可以说:

var type = typeof(AnimalContext<>).MakeGenericType(a.GetType());
var a_Context = Activator.CreateInstance(type);   

请注意,a_context编译时类型为object。您必须将a_context强制转换为定义需要访问的方法的类型或接口。通常,你会看到人们在这里做的是让泛型类型AnimalContext<T>实现一些接口(比如IAnimalContext从定义他们需要的方法的非泛型基类(比如AnimalContext)继承(这样你就可以将a_context强制转换为接口或非泛型基类)。另一种选择是使用dynamic。但是,请记住,在这样做的过程中,泛型类型没有任何好处。

您可以使用MakeGenericType方法使用泛型类型的反射,并利用dynamic关键字:

var type = typeof (AnimalContext<>).MakeGenericType(a.GetType());
dynamic a_Context = Activator.CreateInstance(type);

所以你可以打电话给:

a_Context.DoAnimalStuff();

或者再次使用反射调用方法:

type.GetMethod("DoAnimalStuff").Invoke(a_Context, null);

您需要使用反射创建类型,然后调用该类型。类似于:

Animal a = MyFavoriteAnimal();
var contextType = typeof(EsbRepository<>).MakeGenericType(a.GetType());
dynamic context = Activator.CreateInstance(contextType);
context.DoAnimalStuff();

使用dynamic意味着将在运行时评估上下文变量,从而允许您调用DoAnimalStuff方法。