泛型类,如何在运行时设置类型?

3
我可以为您提供翻译帮助。以下是涉及IT技术的需要翻译的内容:

我创建了一个泛型类,但我在运行时才知道类型,而不是在设计时就知道,因此我想知道如何在运行时设置类型。

例如,我有:

public class MyGenericClass<T>
{
....
}

然后我尝试使用它。我在另一个类中有一个消耗这个泛型类的方法。在这个类的构造函数中,我接收我想要的类型作为参数,所以我有一个类型属性,在其中保存我需要的类型。所以我正在尝试这样做:

MyGenericClass<_typeNeeded> myClass = new MyGenericClass<typeNeeded>();

但这并不起作用。
我如何在运行时中设置自己创建的类的类型?
我正在使用C# 4.0。
谢谢。 Daimroc。
编辑:我想要做的是以下内容。我有一个需要对数据库进行一些查询的类。这些查询总是返回相同的信息,即一个类,但包含此类信息的信息来自不同的表。这是因为我需要确定要使用的查询。为了决定要使用什么查询,我使用我接收到的类型。
正因为这个原因,我不知道设计类型,但是在运行时中可以知道。
我可以使用一个接口,它将由两个类实现,并使用实例化与正确类一起使用的接口,但这使我在实例化时具有开关或if,这就是我尝试避免的,我想要更通用的东西。另外,如果我使用此解决方案,在实例化的瞬间具有if,我可以创建通用类,因此我将只有一个类,这将更易于维护。

3
没有充分的反思是不可能做到的。 - Femaref
你看过这个链接了吗? - Mark M
为什么不使用动态关键字? - Omar
你能具体说明一下吗?你想要达到什么样的结果? - Heisenbug
@Mark M:在这个例子中,我该如何使用这个方法?因为这个方法返回一个对象,但是我必须先声明变量,例如 MyGenericClass<T> miVariable = CreateGeneric...),但问题是我不知道 T 的类型。不过这个例子看起来就是我想要的。 - Álvaro García
研究如何返回强类型变量时,我找到的唯一解决方案是使用表达式方法并使用一个比直接调用慢大约20倍的方法。我建议在使用此方法之前重新考虑如何实现您想要的目标。 - Mark M
4个回答

5

查看MakeGenericType方法。您可以使用它来创建一个Type实例(然后可以使用该类型创建实例),并在运行时确定泛型参数值。请注意,当处理该对象的泛型特性时,您需要更多的反射;您不能在代码中创建变量,其类型是具有变量类型参数的泛型类型。


4
你可以以另一种方式创建你的类,通过将你想要使用的类型传递给构造函数并利用 dynamic 关键字来实现。
例如:
class MyGeneralClass
{
    dynamic myVariable;
    Type type;

    public MyGeneralClass(Type type)
    {  
        this.type = type;
        myVariable = Activator.CreateInstance(type);
        //And then if your type is of a class you can use its methods      
        //e.g. myVariable.MyMethod();
    }

    //If your function return something of type you can also use dynamic
    public dynamic Function()
    {
        return Activator.CreateInstance(type);
    }
}

3

如果你仔细想想,就会发现在运行时确定类型的泛型是没有必要的。泛型提供了强类型检查,但这只有在编译时已知类型时才可能实现。这也是泛型最大的优势之一,因为它提供了安全保障,让你知道对象可以执行哪些动作。如果在运行时你不知道获得的是什么类型,那么这个目的就无法达到,你将不得不深入探索强大(但有时危险)的反射世界。例如,

List<DateTime> list = new List<DateTime>();
foreach (DateTime item in list)
   item.Minute + ": " + item.Hour

因为我在编译时知道类型,所以我可以将其存储在通用容器中,并在使用时具有类型安全性。我知道该项具有 Minute 和 Hour 属性。


如果您想根据对象的类型执行操作,则使用 GetType() 方法。这是您进入运行时类型识别和处理的窗口。

Result PerformQuery(object unknownObject)
{
    switch (object.GetType())
    {
         case typeof(ThisType): Do This; break;
         case typeof(ThatType): Do That; break;
    }
}

如果您要对对象进行操作,则必须在编译时知道方法名称,这意味着您可以使用接口。

IFood hamburger = new Hamburger();
hamburger.BeEaten();

如果您没有或不想使用接口,可以使用反射或动态方法。在这种情况下,如果我知道要调用的方法,我会使用dynamic。

Result PerformQuery(object unknownObject)
{
    if (object.GetType() == typeof(Pizza))  
    {
        dynamic pizza = unknownObject;
        pizza.BakeInOven() // since I checked and know it is type Pizza it is 'somewhat' safe to do this, unless Pizza class changes the name of BakeInOven method.
     }
 }

最后,如果您在编译时不知道要调用的方法,可以使用反射。

  string methodName = CalculateWhatMethodShouldBe();
  MethodInfo myMethod = unknownObject.GetType().GetMethods().First(m => m.Name == methodName);    
  MethodInfo.Invoke(unknownObject);

2

我不太清楚你的代码细节,但也许你可以使用简单的多态:

public interface IMyInterface
{
...
}

public class MyGenericClass<T> : IMyInterface
{
....
}

IMyInterface myClass = new MyGenericClass<typeNeeded>();

您还可以将其与工厂设计模式结合使用,以便其他类根据某些运行时知识负责实例化您的类型。

当然,这要求您的MyGenericClass没有像以下方法一样的方法:

public T GetT(){...}

或者,如果有一个额外的关系将所有T类型联系在一起,使它们都具有共同的超类,比如BaseT,那么你可以改变。
public T GetT(){...}

to:

public BaseT GetT(){...}

3
在 OP 的问题中,typeNeeded 是一个变量,因此 new MyGenericClassy<typeNeeded>() 会编译失败。 - O. R. Mapper
好的,说得不错。无论如何,如果我们知道T不是任何类型而是我们所知道的少数几种类型,并且我们需要简单快速的解决方案(不使用反射),那么我们可以使用一些工厂和switch语句进行类型实例化并坚持使用这个解决方案。 - 0lukasz0
没错。让我们看看原帖作者是否想要走这条路并需要更多信息。 - O. R. Mapper

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