传递实例化的System.Type作为泛型类的类型参数


181

标题有点晦涩。我想知道的是这是否可能:

string typeName = <read type name from somwhere>;
Type myType = Type.GetType(typeName);

MyGenericClass<myType> myGenericClass = new MyGenericClass<myType>();

显然,MyGenericClass被描述为:

public class MyGenericClass<T>

现在,编译器抱怨“找不到类型或名称空间“ myType”。”必须有一种方法可以做到这一点。


泛型!=模板。所有通用类型变量都在编译时而不是在运行时解析。这是4.0动态类型可能有用的情况之一。

1
@Will-用什么方式?当与泛型一起使用时,根据当前的CTP,您实际上最终会调用<object>版本(除非我错过了窍门...)
Marc Gravell

@MarcGravell可以foo.Method((dynamic)myGenericClass)用于运行时方法绑定,有效地用于类型的方法重载的服务定位器模式。
克里斯·马里西克

@ChrisMarisic是的,对于某些泛型public void Method<T>(T obj)-自从发表评论以来,我在过去6年中多次使用过这种技巧; p
Marc Gravell

@MarcGravell是否可以修改该方法,以使该方法实例化?
巴洛普(Barlop)'17年

Answers:


220

您不能没有反思。但是,您可以通过反射来实现。这是一个完整的示例:

using System;
using System.Reflection;

public class Generic<T>
{
    public Generic()
    {
        Console.WriteLine("T={0}", typeof(T));
    }
}

class Test
{
    static void Main()
    {
        string typeName = "System.String";
        Type typeArgument = Type.GetType(typeName);

        Type genericClass = typeof(Generic<>);
        // MakeGenericType is badly named
        Type constructedClass = genericClass.MakeGenericType(typeArgument);

        object created = Activator.CreateInstance(constructedClass);
    }
}

注意:如果泛型类接受多种类型,则在省略类型名称时必须包含逗号,例如:

Type genericClass = typeof(IReadOnlyDictionary<,>);
Type constructedClass = genericClass.MakeGenericType(typeArgument1, typeArgument2);

1
好的,这很好,但是如何在created上调用方法呢?更多反思?
罗伯特·巴思

7
好吧,如果您可以摆脱让您的泛型类型实现非泛型接口的麻烦,则可以强制转换为该接口。或者,你可以写一个做所有你想用通用做的工作你自己的泛型方法,并调用与反思。
乔恩·斯基特

1
是的,如果您仅有关于返回类型的信息在typeArgument类型变量中,我不理解如何使用created?在我看来,您必须将其强制转换为该变量,但您不知道它是什么,因此我不确定是否可以通过反射来实现。如果将对象作为对象变量传递给例如List <int>的示例,是否可以完成此工作,则该对象是否为int类型的另一个问题?将创建的变量视为int吗?
theringostarrs 2009年

6
@ RobertC.Barth您还可以在示例类型中将“创建的”对象键入“动态”,而不是“对象”。这样,您可以在其上调用方法,并且评估将推迟到运行时。
McGarnagle 2012年

4
@balanza:您使用MakeGenericMethod。
乔恩·斯基特

14

不幸的是,没有。泛型参数在编译时必须可解析为1)有效类型或2)另一个泛型参数。没有使用反射的大力支持,就无法基于运行时值创建通用实例。


2

其他一些如何使用剪刀代码运行的方法。假设您有一个类似于

public class Encoder() {
public void Markdown(IEnumerable<FooContent> contents) { do magic }
public void Markdown(IEnumerable<BarContent> contents) { do magic2 }
}

假设在运行时您有一个FooContent

如果您能够在编译时进行绑定,则需要

var fooContents = new List<FooContent>(fooContent)
new Encoder().Markdown(fooContents)

但是,您无法在运行时执行此操作。要在运行时执行此操作,应遵循以下步骤:

var listType = typeof(List<>).MakeGenericType(myType);
var dynamicList = Activator.CreateInstance(listType);
((IList)dynamicList).Add(fooContent);

动态调用 Markdown(IEnumerable<FooContent> contents)

new Encoder().Markdown( (dynamic) dynamicList)

注意dynamic方法调用中的用法。在运行时dynamicListList<FooContent>(另外也是IEnumerable<FooContent>),因为即使对dynamic的使用仍然植根于强类型语言,运行时绑定程序也会选择适当的Markdown方法。如果没有完全匹配的类型,它将查找对象参数方法,如果都不匹配,则会引发运行时绑定程序异常,警告没有方法匹配。

这种方法的明显缺点是在编译时会大大损失类型安全性。尽管如此,沿着这些行的代码将使您以非常动态的方式进行操作,即在运行时仍按您期望的方式完全键入。


2

我的要求略有不同,但希望会对您有所帮助。我需要从配置中读取类型并动态实例化泛型。

namespace GenericTest
{
    public class Item
    {
    }
}

namespace GenericTest
{
    public class GenericClass<T>
    {
    }
}

最后,这是您的称呼方式。使用反引号定义类型

var t = Type.GetType("GenericTest.GenericClass`1[[GenericTest.Item, GenericTest]], GenericTest");
var a = Activator.CreateInstance(t);

0

如果您知道将传递哪种类型,则可以进行反射。switch语句将起作用。显然,这仅在少数情况下有效,但是它比反射要快得多。

public class Type1 { }

public class Type2 { }

public class Generic<T> { }

public class Program
{
    public static void Main()
    {
        var typeName = nameof(Type1);

        switch (typeName)
        {
            case nameof(Type1):
                var type1 = new Generic<Type1>();
                // do something
                break;
            case nameof(Type2):
                var type2 = new Generic<Type2>();
                // do something
                break;
        }
    }
}

一旦您开始处理100多个类,这就会变得很难看。
迈克尔g

0

在此片段中,我想展示如何创建和使用动态创建的列表。例如,我要在此处添加动态列表。

void AddValue<T>(object targetList, T valueToAdd)
{
    var addMethod = targetList.GetType().GetMethod("Add");
    addMethod.Invoke(targetList, new[] { valueToAdd } as object[]);
}

var listType = typeof(List<>).MakeGenericType(new[] { dynamicType }); // dynamicType is the type you want
var list = Activator.CreateInstance(listType);

AddValue(list, 5);

同样,您可以调用列表上的任何其他方法。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.