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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/266115/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-03 20:31:32  来源:igfitidea点击:

Pass An Instantiated System.Type as a Type Parameter for a Generic Class

c#.netgenerics

提问by Robert C. Barth

The title is kind of obscure. What I want to know is if this is possible:

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

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

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

Obviously, MyGenericClass is described as:

显然,MyGenericClass 被描述为:

public class MyGenericClass<T>

Right now, the compiler complains that 'The type or namespace 'myType' could not be found." There has got to be a way to do this.

现在,编译器抱怨“找不到类型或命名空间‘myType’。”必须有办法做到这一点。

采纳答案by Jon Skeet

You can't do this without reflection. However, you cando it with reflection. Here's a complete example:

如果没有反思,你就无法做到这一点。但是,您可以通过反射来实现。这是一个完整的例子:

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);
    }
}

Note: if your generic class accepts multiple types, you must include the commas when you omit the type names, for example:

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

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

回答by JaredPar

Unfortunately no there is not. Generic arguments must be resolvable at Compile time as either 1) a valid type or 2) another generic parameter. There is no way to create generic instances based on runtime values without the big hammer of using reflection.

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

回答by Chris Marisic

Some additional how to run with scissors code. Suppose you have a class similar to

一些额外的如何使用剪刀代码运行。假设你有一个类似于

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

Suppose at runtime you have a FooContent

假设在运行时你有一个FooContent

Ifyou were able to bind at compile time you would want

如果您能够在编译时绑定,您会想要

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

Howeveryou cannot do this at runtime. To do this at runtime you would do along the lines of:

但是,您不能在运行时执行此操作。要在运行时执行此操作,您将执行以下操作:

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

To dynamically invoke Markdown(IEnumerable<FooContent> contents)

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

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

Note the usage of dynamicin the method call. At runtime dynamicListwill be List<FooContent>(additionally also being IEnumerable<FooContent>) since even usage of dynamic is still rooted to a strongly typed language the run time binder will select the appropriate Markdownmethod. If there is no exact type matches, it will look for an object parameter method and if neither match a runtime binder exception will be raised alerting that no method matches.

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

The obvious draw back to this approach is a huge loss of type safety at compile time. Nevertheless code along these lines will let you operate in a very dynamic sense that at runtime is still fully typed as you expect it to be.

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

回答by Master P

My requirements were slightly different, but will hopefully help someone. I needed to read type from a config and instantiate the generic type dynamically.

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

namespace GenericTest
{
    public class Item
    {
    }
}

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

Finally, here is how you call it. Define the type with a backtick.

最后,这是你如何称呼它的。用反引号定义类型

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

回答by Todd Skelton

If you know what types will be passed you can do this without reflection. A switch statement would work. Obviously, this would only work in a limited number of cases, but it'll be much faster than reflection.

如果您知道将传递哪些类型,则无需反射即可执行此操作。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;
        }
    }
}

回答by EGN

In this snippet I want to show how to create and use a dynamically created list. For example, I'm adding to the dynamic list here.

在这个片段中,我想展示如何创建和使用动态创建的列表。例如,我在这里添加到动态列表。

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);

Similarly you can invoke any other method on the list.

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