如何在C#中通过字符串创建一个通用类?

20

我有一个如下所示的通用类:

public class Repository<T> {...}

我需要用一个字符串实例化它...

例如:

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

我该如何做到这一点?那是否可能?

谢谢!


请注意,在程序中您不能引用Repository<sample>,因此_rep的类型必须是“object”或所有Repository<T>通用的某个接口。 - Anton Tykhyy
9个回答

27

这是我的个人意见:

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

我该如何访问存储库方法? - Paul
3
你可以使用反射来创建在运行时才知道的类型的存储库,这与之前的方式相同。如果你想要灵活性,就必须为此付出代价。 - alex
你能展示给我如何做吗?谢谢! - Paul

17

首先使用Type.GetType(stringContainingTheGenericTypeArgument)获取类型对象。

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

最后使用Activator.CreateInstance


真正的问题不是“他应该吗?”吗? - C. Ross
从字符串到泛型类型?也许是这样。然而,MakeGenericType经常用于linq提供程序和其他表达式树消费代码中。它是一个很好的帮手,尽管对于日常LOB应用程序来说当然不是什么需要担心的事情。 - Frans Bouma

3

这是一个需要使用C# 4.0中的dynamic关键字的任务。

这里有一些不错的技巧可以让您得到对象实例,但无论如何,当前版本的C#只知道它有一个object,并且它本身不能做太多事情,因为当前的C#不支持晚期绑定。您至少需要将其转换为已知类型才能对其进行任何操作。最终,您必须在编译时知道所需的类型,否则就会失败。

现在,如果您可以将存储库类限制为实现某些已知接口的类型,则处于更好的状态。


所以,我没有解决我的问题... - Paul
不行,我的观点是,在C# 4发布之前,除非你能重新思考你的架构,否则你不会得到一个真正可行的方案。 - Joel Coehoorn
2
如果您分享您打算使用这些存储库对象做什么,我们可能能够帮助您设计出更好的方案。 - Joel Coehoorn

2
如果我理解你的问题正确...您想做的是在运行时构建特定的泛型实现,其类型为(Repository<T>)?
如果是这样,请查看MakeGenericType。您可以使用typeof(Repository)和您想要的对象T的System.Type来构建它。一旦您拥有了该类型,Activator.CreateInstance将对您起作用。

2
假设你的字符串保存了一个类型的名称,你可以这样写:
object _rep = Activator.CreateInstance(typeof(Repository<>).MakeGenericType(Type.GetType(_sample)));

然而,_rep将是一个未经类型化的对象,你将没有任何方法对其进行操作。由于在编译时不知道泛型类的类型,所以没有类型可以将其转换。(除非Repository继承一个非泛型类或实现一个非泛型接口)
你可以通过为Repository创建一个非泛型基类并将对象转换为该基类来解决这个问题。
但是,根据您的情况,您可能希望将Repository变成一个非泛型类。
如果Repository是包含其他类的类,则最好将其变为非泛型。您可以使其构造函数接受一个Type对象,然后在添加每个对象时调用Type.IsInstanceOfType来确保它是正确的类型。
如果Repository是一组分类的集合,则最好将其变为非泛型,并使其构造函数接受一个字符串类别。
如果您想要更具体的建议,请发布有关您情况的更多详细信息。

我的代码库: public class Repository<T> : RepositoryWithTypedId<T, int>, IRepository<T> { }public class RepositoryWithTypedId<T, IdT> : IRepositoryWithTypedId<T, IdT>而且我无法更改这个实现... 那么这是不可能的吗? - Paul
我的_rep是一个DAO(数据访问对象)... - Paul
但是为什么在编译时你不知道类型名称呢?你可能想要将整个包含 _rep 的类或方法设置为泛型。 - SLaks
用户选择了类名为“City”的RADIO按钮… 因此,我需要实例化Repository<City>,并从数据库中获取所有城市以显示出来… - Paul
你可以使用我在答案中编写的代码行来实例化Repository<City>。我不知道你想让City对象显示在什么地方(网格?),但是通过将我的代码行中的对象提供给网格(?),你应该能够做到这一点。 - SLaks
显示剩余7条评论

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

这将创建一个 Repository<string> 的实例。您可以将 "System.String" 替换为任何自己想要的类型。


0

这是“有点儿”可能的。有点儿意味着你可以做到,但这有点像黑客。

你有三个选项:

如果你事先知道你的类,请使用 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,这将节省您进行强制转换的步骤。

考虑到类型是使用字符串进行构建的,C# 如何合理地将特定类型与结果关联起来? - Daniel Earwicker

0

泛型适用于类型,而不是实例。您可以在此处阅读更多信息。

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

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

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

-3
在泛型类中,“T”代表类型,而不是类型的实例。因此,如果您希望存储字符串对象,则应使用:
var _rep = new Repository<string>();

我认为你误解了问题 - 这个字符串包含一个类型名称。 - Daniel Earwicker

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