带有示例的Activator.CreateInstance的目的?


124

有人可以解释 Activator.CreateInstance()详细目的吗?


6
通用重载中可能没有更多的文档和示例。我鼓励from中的文档CreateInstance(Type type)CreateInstance<T>()重载匹配。
ClausJørgensen

4
MSDN页面上只有一条说明行,“包含用于在本地或远程创建对象类型或获取对现有远程对象的引用的方法。不能继承此类。” msdn.microsoft.com/en-us/library/system.activator.aspx
gonzobrains

2
如果您来自Java背景,这就是c#.net这样做的方法Object xyz = Class.forName(className).newInstance();
SNag

有它更好的文档在这里
jpaugh

Answers:


149

假设您在MyFancyObject下面有一个像这样的课:

class MyFancyObject
{
 public int A { get;set;}
}

它可以让您打开:

String ClassName = "MyFancyObject";

进入

MyFancyObject obj;

使用

obj = (MyFancyObject)Activator.CreateInstance("MyAssembly", ClassName))

然后可以执行以下操作:

obj.A = 100;

这就是它的目的。它还有许多其他重载,例如Type在字符串中提供“ 代替”类名。为什么会有这样的问题是另外一个故事。这是一些需要它的人:


2
事实证明这对我很有用。就我而言,该类位于另一个名称空间中,因此我必须确保将名称空间包括在我的ClassName字符串(即String ClassName = "My.Namespace.MyFancyObject";)中。
Scott

1
您忘记添加Unwrap()。您也可以输入null,而不是“ MyAssembly”,系统将搜索当前的Assembly。
Tony

1
我可以做这样的事情,obj = (MyFancyObject)Activator.CreateInstance("MyAssembly", ClassName))而不是用类型强制转换。使用由ClassName制成的类型进行转换?这样Type type = Type.GetType(ClassName);obj = (type )Activator.CreateInstance("MyAssembly", ClassName))吗?
rluks 2015年

1
以上内容与以下内容有何不同:MyFancyObject obj = new MyFancyObject?
Ed Landau'9

1
@Ed Landau的区别在于,您可以在运行时实例化一个对象,而该对象在编译时不知道其类型。例如,如果您正在为程序构建插件系统。答案中的链接可以为您提供一些有关何时无法仅编写MyFancyObject obj = new MyFancyObject()的想法。通常,这通常与反射的使用结合在一起。您也可以查看stackoverflow.com/questions/9409293/…以获得其他描述。
deepee1 2013年

47

好吧,我可以举一个例子,说明为什么要使用类似的东西。想想一个游戏,您想要在XML文件中存储关卡和敌人。解析此文件时,您可能会有这样的元素。

<Enemy X="10" Y="100" Type="MyGame.OrcGuard"/>

您现在可以做的是,动态创建在关卡文件中找到的对象。

foreach(XmlNode node in doc)
   var enemy = Activator.CreateInstance(null, node.Attributes["Type"]);

这对于构建动态环境非常有用。当然,也可以将其用于插件或插件方案以及更多其他方案。


5
我知道这是创建一个类型的新实例,但这是一个很好的简单示例,说明了为什么要这样做。
jamiebarrow

14

我的好朋友MSDN 可以举例说明

以下是代码,以防将来链接或内容发生更改:

using System;

class DynamicInstanceList
{
    private static string instanceSpec = "System.EventArgs;System.Random;" +
        "System.Exception;System.Object;System.Version";

    public static void Main()
    {
        string[] instances = instanceSpec.Split(';');
        Array instlist = Array.CreateInstance(typeof(object), instances.Length);
        object item;
        for (int i = 0; i < instances.Length; i++)
        {
            // create the object from the specification string
            Console.WriteLine("Creating instance of: {0}", instances[i]);
            item = Activator.CreateInstance(Type.GetType(instances[i]));
            instlist.SetValue(item, i);
        }
        Console.WriteLine("\nObjects and their default values:\n");
        foreach (object o in instlist)
        {
            Console.WriteLine("Type:     {0}\nValue:    {1}\nHashCode: {2}\n",
                o.GetType().FullName, o.ToString(), o.GetHashCode());
        }
    }
}

// This program will display output similar to the following: 
// 
// Creating instance of: System.EventArgs 
// Creating instance of: System.Random 
// Creating instance of: System.Exception 
// Creating instance of: System.Object 
// Creating instance of: System.Version 
// 
// Objects and their default values: 
// 
// Type:     System.EventArgs 
// Value:    System.EventArgs 
// HashCode: 46104728 
// 
// Type:     System.Random 
// Value:    System.Random 
// HashCode: 12289376 
// 
// Type:     System.Exception 
// Value:    System.Exception: Exception of type 'System.Exception' was thrown. 
// HashCode: 55530882 
// 
// Type:     System.Object 
// Value:    System.Object 
// HashCode: 30015890 
// 
// Type:     System.Version 
// Value:    0.0 
// HashCode: 1048575

1
MSDN不太可能发生,并且在此处复制内容几乎侵犯了版权;)
ClausJørgensen13年

13
你是对的。我个人认为该原则也可以给出更好的答案。在我当前的项目中,我经常带着很多想法来到SO。我通常只需要一个简单且切合实际的答案,这样我就可以从上次停止的地方继续。我讨厌不得不打开文章,有时甚至链接到其他文章。许多回答者没有意识到,很多人没有花时间来这里阅读几篇文章,没有大量不必要的介绍和演讲。简要总结好文章的重要部分是我所见过的一些最佳答案的关键。
Aske B.

10

您也可以这样做-

var handle = Activator.CreateInstance("AssemblyName", 
                "Full name of the class including the namespace and class name");
var obj = handle.Unwrap();

Unwrap()是缺少的部分。:)
托尼

我找不到上面要使用的“ Unwrap()”方法(如上所述)。新的.NET API是否有所更改?
山姆

它仍然在系统名称空间中。
威廉

2
您能否提供一些其他解释,以.Unwrap()准确解释什么以及与其他解决方案的关系?
Jeroen Vannevel,2015年

@Sam CreateInstance在返回的位置上有不同的重载System.Runtime.Remoting.ObjectHandle
nawfal

9

接下来是一个很好的例子:例如,您有一组Logger,并且允许用户通过配置文件指定要在运行时使用的类型。

然后:

string rawLoggerType = configurationService.GetLoggerType();
Type loggerType = Type.GetType(rawLoggerType);
ILogger logger = Activator.CreateInstance(loggerType.GetType()) as ILogger;

或者另一种情况是,当您有一个公共实体工厂,该工厂创建实体并且还负责通过从DB接收到的数据来初始化实体时:

(伪代码)

public TEntity CreateEntityFromDataRow<TEntity>(DataRow row)
 where TEntity : IDbEntity, class
{
   MethodInfo methodInfo = typeof(T).GetMethod("BuildFromDataRow");
   TEntity instance = Activator.CreateInstance(typeof(TEntity)) as TEntity;
   return methodInfo.Invoke(instance, new object[] { row } ) as TEntity;
}

这不起作用,typeof(loggerType)导致loggerType is a variable and used like a type
Barkermn01 '17

3

Activator.CreateInstance方法使用与指定参数最匹配的构造函数创建指定类型的实例。

例如,假设您将类型名称作为字符串,并且想要使用该字符串创建该类型的实例。您可以Activator.CreateInstance为此:

string objTypeName = "Foo";
Foo foo = (Foo)Activator.CreateInstance(Type.GetType(objTypeName));

这是MSDN文章,它更详细地说明了其应用程序:

http://msdn.microsoft.com/zh-CN/library/wccyzw83.aspx


5
或者您可以使用new Foo()。我认为OP需要一个更现实的例子。
康拉德·鲁道夫

1
我同意@Konrad。使用的原因CreateInstance是,如果您不知道要在设计时实例化的对象类型。在此示例中,Foo由于将其强制转换为type ,因此您清楚地知道了它的类型Foo。您永远不会这样做,因为您可以做到Foo foo = new Foo()
theyetiman 2013年

1

建立在deepee1和this的基础上,这是如何在字符串中接受类名,然后使用它通过LINQ读写数据库。我使用“ dynamic”而不是deepee1的强制转换,因为它允许我分配属性,这使我们可以动态选择并在所需的任何表上进行操作。

Type tableType = Assembly.GetExecutingAssembly().GetType("NameSpace.TableName");
ITable itable = dbcontext.GetTable(tableType);

//prints contents of the table
foreach (object y in itable) {
    string value = (string)y.GetType().GetProperty("ColumnName").GetValue(y, null);
    Console.WriteLine(value);
}

//inserting into a table
dynamic tableClass = Activator.CreateInstance(tableType);
//Alternative to using tableType, using Tony's tips
dynamic tableClass = Activator.CreateInstance(null, "NameSpace.TableName").Unwrap();
tableClass.Word = userParameter;
itable.InsertOnSubmit(tableClass);
dbcontext.SubmitChanges();

//sql equivalent
dbcontext.ExecuteCommand("INSERT INTO [TableNme]([ColumnName]) VALUES ({0})", userParameter);

0

如果您已经知道该课程并打算进行铸造,那么为什么要使用它呢?为什么不只是按照老式的方式来做,并让班级像您通常那样做?这样做比正常完成没有优势。有没有办法获取文本并对其进行操作:

label1.txt = "Pizza" 
Magic(label1.txt) p = new Magic(lablel1.txt)(arg1, arg2, arg3);
p.method1();
p.method2();

如果我已经知道它是披萨,那就没有优势了:

p = (Pizza)somefancyjunk("Pizza"); over
Pizza p = new Pizza();

但我认为Magic方法(如果存在)具有巨大优势。


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.