从字符串中创建一个类的实例

273

有没有一种方法可以根据我在运行时知道的类名创建一个类的实例。基本上,我会在字符串中拥有类的名称。


你似乎已经描述了你想要实现的解决方案,但并没有说明你试图解决的问题。也许你正在尝试使用可扩展性来做某些事情,如果是这样,我建议你查看托管可扩展框架 - Jay Bazuzi
8个回答

183

16
与优秀的示例相关:https://dev59.com/-3RB5IYBdhLWcg3w1Kr0 - John S.
此外,这篇文章也很相关,因为需要找到您的类型:https://dev59.com/SHI-5IYBdhLWcg3wf4dD - Brad Parks
1
示例:https://dev59.com/Cmsz5IYBdhLWcg3w47-m - profimedica
4
好的,我会尽力进行翻译。以下为您需要翻译的内容:例如:var driver = (OpenQA.Selenium.IWebDriver)Activator.CreateInstance("WebDriver", "OpenQA.Selenium.Firefox.FirefoxDriver").Unwrap(); - Endy Tjahjono
2
重要提示:使用 .Unwrap() 来获取远程处理句柄,以便您实际执行强制转换。@Endy - 谢谢 - Roger Willcocks

125
很简单。假设您的类名为Car,命名空间为Vehicles,那么将参数传递为Vehicles.Car,它将返回类型为Car的对象。这样,您就可以动态创建任何类的任何实例。
public object GetInstance(string strFullyQualifiedName)
{         
     Type t = Type.GetType(strFullyQualifiedName); 
     return  Activator.CreateInstance(t);         
}

如果您的完全限定名称(即,在此情况下为Vehicles.Car)位于另一个程序集中,则Type.GetType将为null。在这种情况下,您需要循环遍历所有程序集并找到Type。为此,您可以使用以下代码。
public object GetInstance(string strFullyQualifiedName)
{
     Type type = Type.GetType(strFullyQualifiedName);
     if (type != null)
         return Activator.CreateInstance(type);
     foreach (var asm in AppDomain.CurrentDomain.GetAssemblies())
     {
         type = asm.GetType(strFullyQualifiedName);
         if (type != null)
             return Activator.CreateInstance(type);
     }
     return null;
 }

现在,如果您想调用一个带参数的构造函数,请按照以下步骤进行。
Activator.CreateInstance(t,17); // Incase you are calling a constructor of int type

替代

Activator.CreateInstance(t);

如何在不进行强制转换的情况下使用它,以及如何从给定的字符串中进行强制转换? - TaW
2
@TaW - 为了使用类实例,您需要对它将要执行的操作有一定的了解,否则您将无法使用它。最常见的用例是将其转换为某个接口,该接口提供了预定义的契约。(除非您正在使用dynamic代码-请参见https://dev59.com/P3E85IYBdhLWcg3wnU4d#2690661) - Tomer Cagan
2
不要在变量名中编码变量类型,例如:没有必要在 strFullyQualifiedName 前面加上 strfullyQualifiedName 就足够了。 - Mehdi Dehghani
关键字 str 作为变量命名约定的一部分被使用。某些组织和项目坚持遵循这一约定,因此我也使用了它。如果您曾在某些组织/项目中工作过,您就会知道这一点。正如您所说,没有 str 也可以完成工作 :) @MehdiDehghani - Sarath Subramanian
1
我知道,不需要在任何组织工作就能了解命名规范,尽管这种规范被称为[匈牙利命名法](https://en.wikipedia.org/wiki/Hungarian_notation),但它是最糟糕的命名规范之一,特别是对于C#。 - Mehdi Dehghani
显示剩余2条评论

58

我已经成功地使用了这种方法:

System.Reflection.Assembly.GetExecutingAssembly().CreateInstance(string className)

您需要将返回的对象转换为所需的对象类型。


13
我正在想象一个场景,通过类名创建对象然后将其强制转换为该类型会有任何意义。 - MusiGenesis
14
我理解你的意思。这似乎是多余的。如果你已经知道类名,为什么还需要动态字符串?可能有一种情况是你正在将其转换为基类,并且该字符串表示该基类的子类。 - Ray Li
4
如果已知基类,您可以使用基类或其接口作为参数将派生类传递给函数而无需使用反射。 - That Realty Programmer Guy
3
有用的情况是:你只需要序列化接口或其他相当常见的接口。你将不会将其强制转换为类,但至少要转换为比对象更具体的东西。 - Harald Coppoolse
Xaml 点击跳转到新导航页面是一个完美的例子,我还可以想到很多。 - Oliver Dixon
3
如何将给定的字符串进行类型转换(cast)? - TaW

27

我的问题可能应该更具体一些。实际上,我知道一个字符串的基类,所以我通过以下方式解决了它:

ReportClass report = (ReportClass)Activator.CreateInstance(Type.GetType(reportClass));

Activator.CreateInstance类有不同的方法可以以不同的方式实现相同的功能。我本可以将其转换为对象,但上述方法对我的情况最有用。


4
建议您编辑问题并注明更改,而不是在问题部分作出回应。这样做可以获得更多/更好的答案。 - Jason Jackson
感谢您发布了能够解决问题的具体代码行。在处理所有CreateInstance重载和生成类型的不同方式时,我花费了很多时间,而您为我节省了这些时间。 - Ethel Evans

7

要从解决方案中的另一个项目创建类的实例,您可以获取任何类(例如BaseEntity)指示的程序集并创建新实例:

  var newClass = System.Reflection.Assembly.GetAssembly(typeof(BaseEntity)).CreateInstance("MyProject.Entities.User");

虽然其他人回答了这个问题,但这是一个非常好的答案,解决了在我的情况下如何确保你有适当的程序集,如果你知道一个兄弟类。太棒了。 - Bernesto

4
我知道我来晚了...但你正在寻找的解决方案可能是以上内容的结合,以及使用接口来定义您的对象公开可访问的方面。
然后,如果所有通过这种方式生成的类都实现了该接口,您只需将其强制转换为接口类型并处理生成的对象即可。

3
例如,如果您在数据库字段中存储各种类型的值(以字符串形式存储),并且有另一个字段带有类型名称(即String、bool、int、MyClass),那么从该字段数据中,您可以使用上述代码创造任何类型的类,并用第一个字段的值填充它。当然,这取决于您正在存储的类型是否具有将字符串解析为正确类型的方法。我已经多次使用此功能来在数据库中存储用户首选项设置。

-14
ReportClass report = (ReportClass)Activator.CreateInstance(Type.GetType(reportClass));

为什么要编写这样的代码?如果有一个类'ReportClass'可用,您可以直接实例化它,如下所示。

ReportClass report = new ReportClass();

当您没有必要的类可用,但希望动态实例化或调用方法时,代码ReportClass report = (ReportClass)Activator.CreateInstance(Type.GetType(reportClass)); 会被使用。

也就是说,在编写代码时您不具备ReportClass类,但知道程序集时它是有用的。


ReportClass可以是基类或接口。 - JSON

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