如何从C#中的字符串创建泛型类?

时间:2009-03-26 20:04:31

标签: c# .net reflection generics

我有一个类似的Generic类:

public class Repository<T> {...}

我需要用字符串来实例化... 示例:

string _sample = "TypeRepository";
var _rep = new Repository<sample>();

我该怎么做?这甚至可能吗?

谢谢!

9 个答案:

答案 0 :(得分:24)

这是我的2美分:

Type genericType = typeof(Repository<>);
Type[] typeArgs = { Type.GetType("TypeRepository") };
Type repositoryType = genericType.MakeGenericType(typeArgs);

object repository = Activator.CreateInstance(repositoryType);

回答评论中的问题。

MethodInfo genericMethod = repositoryType.GetMethod("GetMeSomething");
MethidInfo closedMethod = genericMethod.MakeGenericMethod(typeof(Something));
closedMethod.Invoke(repository, new[] { "Query String" });

答案 1 :(得分:16)

首先使用Type.GetType(stringContainingTheGenericTypeArgument)

获取Type对象

然后使用typeof(Repository<>).MakeGenericType(theTypeObject)获取通用类型。

最后使用Activator.CreateInstance

答案 2 :(得分:3)

这是C#4.0中dynamic关键字的作业。

这里有一些不错的黑客可以让你获得你的对象的实例,但无论当前版本的C#只会知道它有一个object,它对一个对象无能为力因为当前的C#不支持后期绑定。您需要能够至少将其转换为已知类型以对其执行任何操作。最终,您必须在编译时知道所需的类型,否则您将失去运气。

现在,如果您可以将您的存储库类限制为实现某个已知接口的类型,那么您的状态会更好。

答案 3 :(得分:2)

如果我正确理解你的问题......你要做的是拿你的类型(Repository&lt; T&gt;)并在运行时构建一个特定的通用实现?

如果是这样,请查看MakeGenericType。您可以使用typeof(Repository)和T所需对象的System.Type并以这种方式构造它。一旦你有了类型,Activator.CreateInstance将适合你。

答案 4 :(得分:2)

假设您的字符串包含类型的名称,您可以编写

object _rep = Activator.CreateInstance(typeof(Repository<>).MakeGenericType(Type.GetType(_sample)));

但是,_rep将是一个无类型的对象,你将无法用它做任何事情。由于您不知道泛型类在编译时的类型,因此没有将其强制转换为的类型。 (除非Repository继承非泛型类或实现非泛型接口)

您可以通过为Repository创建一个非泛型基类并将对象转换为它来解决这个问题。

但是,根据您的情况,您可能希望Repository成为非泛型类。

如果Repository是包含其他类的类,则最好将其设为非泛型。您可以使其构造函数采用Type对象,然后在您添加的每个对象上调用Type.IsInstanceOfType以确保它是正确的类型。

如果Repository是事物的分类集合,那么最好将其设置为非泛型,并使其构造函数采用string category

如果您需要更具体的建议,请发布有关您情况的更多详细信息。

答案 5 :(得分:1)

Type repType = typeof(Repository <>).MakeGenericType(Type.GetType("System.String"));
object rep = Assembly.GetAssembly(repType).CreateInstance(repType.FullName);

这将创建Repository<string>的实例。您可以将“System.String”替换为您喜欢的任何类型。

答案 6 :(得分:0)

这有点“可行”。你可以做到这一点,但这有点像黑客。

您有3个选项:

如果您事先知道您的课程,请使用switch语句。基本上是这样的:

switch(str){
   case "TypeRepository": return new Repository<TypeRepository>;
}

作为上述的更高级形式,您可以使用字典而不是哈希表

var factory = new Dictionary<string, Func<object>>();
factory.Add( "TypeRepository", () => new Repository<TypeRepository>() );

var theObject = factory["TypeRepository"]() as Repository<TypeRepository>;

为了获得最大的灵活性,您可以使用反射在运行时将字符串与类匹配。请注意,反射速度相当慢,因此如果您以任何规律性进行此操作,则需要避免反射。例如,这是使用Frans Bouma's方法的一个。只需在代码中将List<>更改为Repository<>

public static object MakeList( string typeStr )
{
    var nameSpace = "MyTestApplication" + ".";

    var objType = Type.GetType(nameSpace + typeStr); // NAMESPACE IS REQUIRED
    var listType = typeof(List<>).MakeGenericType(objType);
    return Activator.CreateInstance(listType);
}

var listOfThings = MakeList("MyCoolObject") as List<MyCoolObject>;

注意:没有办法避免所有这些机制返回object这一事实,然后你必须将其转换为正确的类型,而不是返回只返回强类型值。

这是不可避免的,因为在运行时你不知道列表的类型,而C#是在编译时知道事物的(这就是人们说“静态类型编程语言”时的意思)。在C#4中它会减少痛苦,你将能够返回dynamic,这将为你节省一些力量。

答案 7 :(得分:0)

泛型可以处理类型,而不是实例。您可以阅读更多here

听起来你只需要在你的类中添加一个构造函数,它接受所需类型并初始化值:

public class Foo<T>
{
    private T = default(T);

    public Foo(T initValue)
    {
        _val = T;
    }
}

答案 8 :(得分:-3)

Generic类中的“T”代表类型,而不是类型的实例。因此,如果您希望存储库保存字符串对象,请使用

var _rep = new Repository<string>();