2008-08-03 27 views

Trả lời

685

Các Activator lớp trong không gian tên gốc System là khá mạnh mẽ.

Có rất nhiều quá tải để truyền tham số cho hàm tạo và như vậy. Kiểm tra các tài liệu hướng dẫn tại địa chỉ:

http://msdn.microsoft.com/en-us/library/system.activator.createinstance.aspx

hoặc (đường mới)

https://docs.microsoft.com/en-us/dotnet/api/system.activator.createinstance

Dưới đây là một số ví dụ đơn giản:

ObjectType instance = (ObjectType)Activator.CreateInstance(objectType); 

ObjectType instance = (ObjectType)Activator.CreateInstance("MyAssembly","MyNamespace.ObjectType"); 
+16

Vui mừng khi cuối cùng đã tìm thấy điều này, nhưng cuộc gọi thứ hai là không chính xác, thiếu một trích dẫn và parms đảo ngược, nên là: ObjectType instance = (ObjectType) Activator.CreateInstance ("MyAssembly", "MyNamespace.ObjectType"); – kevinc

+8

Bạn cần gọi 'Unwrap()' để lấy loại đối tượng thực tế bạn muốn: ConcreteType instance = (ConcreteType) Activator.CreateInstance (objectType) .Unwrap(); –

34

Một thực hiện vấn đề này là để cố gắng gọi constructor tham số-less của Type:

public static object GetNewObject(Type t) 
{ 
    try 
    { 
     return t.GetConstructor(new Type[] { }).Invoke(new object[] { }); 
    } 
    catch 
    { 
     return null; 
    } 
} 

Đây là phương pháp tương tự, chứa trong một phương pháp chung:

public static T GetNewObject<T>() 
{ 
    try 
    { 
     return (T)typeof(T).GetConstructor(new Type[] { }).Invoke(new object[] { }); 
    } 
    catch 
    { 
     return default(T); 
    } 
} 
+15

Lập trình điều khiển ngoại lệ? Điều này có vẻ như thực hiện rất kém khi bạn chỉ đơn giản có thể phản ánh qua loại để xác định các nhà thầu. – Firoso

11

Nếu điều này dành cho thứ gì đó sẽ được gọi rất nhiều trong một phiên bản ứng dụng, việc biên dịch và lưu mã động nhanh hơn rất nhiều thay vì sử dụng trình kích hoạt hoặc ConstructorInfo.Invoke(). Hai tùy chọn dễ dàng để biên dịch động được biên dịch Linq Expressions hoặc một số đơn giản IL opcodes and DynamicMethod. Dù bằng cách nào, sự khác biệt là rất lớn khi bạn bắt đầu nhận được vào các vòng chặt chẽ hoặc nhiều cuộc gọi.

+0

"Liên kết mã vạch IL và DynamicMethod" đã chết. –

111
ObjectType instance = (ObjectType)Activator.CreateInstance(objectType); 

Lớp Activator có một biến thể chung mà làm cho điều này một chút dễ dàng hơn:

ObjectType instance = Activator.CreateInstance<ObjectType>(); 
+9

Ngoại trừ việc này không hoạt động trong thời gian chạy 'Loại t'. –

+3

@Kevin Tất nhiên. Thao tác như vậy * không thể * hoạt động với ngôn ngữ được nhập tĩnh bởi vì nó không có ý nghĩa. Bạn không thể gọi các phương thức trên một đối tượng kiểu không xác định.Trong khi chờ đợi (= kể từ khi viết câu trả lời này) C# đã có cấu trúc 'động' mà * cho phép * cho phép các cấu trúc như vậy nhưng đối với hầu hết các mục đích, câu trả lời này vẫn đề cập đến nó. –

+0

@KonradRudolph Không hoàn toàn đúng. Đầu tiên của C# * không * cho phép bạn tạo các kiểu mới khi chạy. Bạn chỉ không thể gọi bất cứ điều gì trên chúng * theo một cách an toàn tĩnh *. Vì vậy, yeah, bạn là một nửa chính xác. Nhưng thực tế hơn bạn cần điều này khi bạn nạp các assembly vào thời gian chạy, điều đó có nghĩa là kiểu không được biết tại thời gian biên dịch. C# sẽ bị giới hạn nghiêm trọng nếu bạn không thể làm điều này. Ý tôi là bạn đã tự chứng minh điều đó: phương pháp Activator khác có thể thực hiện một công việc kiểu như thế nào? Khi MS viết lớp Activator, họ không có kiến ​​thức biên dịch về bất kỳ kiểu người dùng nào trong tương lai sẽ viết. – AnorZaken

7

Dường như khu chung T t = new T(); làm việc?

+7

Trên thực tế, nó sẽ trong một lớp học chung/phương pháp, nhưng không phải cho một "Loại" nhất định. –

3
public AbstractType New 
{ 
    get 
    { 
     return (AbstractType) Activator.CreateInstance(GetType()); 
    } 
} 
7

Nếu bạn muốn sử dụng hàm tạo mặc định thì giải pháp sử dụng System.Activator được trình bày trước đó có lẽ là tiện lợi nhất. Tuy nhiên, nếu loại thiếu một hàm tạo mặc định hoặc bạn phải sử dụng một hàm không mặc định, thì một tùy chọn là sử dụng sự phản chiếu hoặc System.ComponentModel.TypeDescriptor. Trong trường hợp của sự phản ánh, nó là đủ để chỉ biết tên loại (với không gian tên của nó).

Ví dụ sử dụng phản ánh:

ObjectType instance = 
    (ObjectType)System.Reflection.Assembly.GetExecutingAssembly().CreateInstance(
     typeName: objectType.FulName, // string including namespace of the type 
     ignoreCase: false, 
     bindingAttr: BindingFlags.Default, 
     binder: null, // use default binder 
     args: new object[] { args, to, constructor }, 
     culture: null, // use CultureInfo from current thread 
     activationAttributes: null 
    ); 

Ví dụ sử dụng TypeDescriptor:

ObjectType instance = 
    (ObjectType)System.ComponentModel.TypeDescriptor.CreateInstance(
     provider: null, // use standard type description provider, which uses reflection 
     objectType: objectType, 
     argTypes: new Type[] { types, of, args }, 
     args: new object[] { args, to, constructor } 
    ); 
9

của nó khá đơn giản. Giả sử rằng tên lớp của bạn là Car và không gian tên là Vehicles, sau đó chuyển tham số là Vehicles.Car trả về đối tượng thuộc loại Car. Như thế này bạn có thể tạo bất kỳ cá thể nào của bất kỳ lớp nào theo kiểu động.

public object GetInstance(string strNamesapace) 
{   
    Type t = Type.GetType(strNamesapace); 
    return Activator.CreateInstance(t);   
} 

Nếu bạn Fully Qualified Name (ví dụ, Vehicles.Car trong trường hợp này) là trong lắp ráp khác, Type.GetType sẽ được null. Trong những trường hợp như vậy, bạn có vòng lặp qua tất cả các hội đồng và tìm số Type. Để làm điều đó, bạn có thể sử dụng mã bên dưới

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

Và bạn có thể nhận được cá thể bằng cách gọi phương thức trên.

object objClassInstance = GetInstance("Vehicles.Car"); 
+0

Trong trường hợp thứ hai của bạn (lắp ráp bên ngoài), bạn chỉ có thể vượt qua trong "Vehicles.Car, OtherAssembly" để phương pháp đầu tiên của bạn và nó sẽ làm việc. Rõ ràng OtherAssembly là tên của hội đồng mà nó sống. – danmiser

+1

@danmiser Điều đó cần mã hóa cứng tên lắp ráp. Để thực hiện tính linh hoạt, tôi kiểm tra null và mã hoạt động theo cách năng động :) –

3

tôi có thể qua câu hỏi này bởi vì tôi đã tìm cách để thực hiện một phương pháp đơn giản CloneObject cho lớp tùy ý (với một constructor mặc định)

Với phương pháp chung chung, bạn có thể yêu cầu loại thực mới().

Public Function CloneObject(Of T As New)(ByVal src As T) As T 
    Dim result As T = Nothing 
    Dim cloneable = TryCast(src, ICloneable) 
    If cloneable IsNot Nothing Then 
     result = cloneable.Clone() 
    Else 
     result = New T 
     CopySimpleProperties(src, result, Nothing, "clone") 
    End If 
    Return result 
End Function 

Giả sử không phải loại chung, có một hàm tạo mặc định và bắt giữ ngoại lệ nếu không.

Public Function CloneObject(ByVal src As Object) As Object 
    Dim result As Object = Nothing 
    Dim cloneable As ICloneable 
    Try 
     cloneable = TryCast(src, ICloneable) 
     If cloneable IsNot Nothing Then 
      result = cloneable.Clone() 
     Else 
      result = Activator.CreateInstance(src.GetType()) 
      CopySimpleProperties(src, result, Nothing, "clone") 
     End If 
    Catch ex As Exception 
     Trace.WriteLine("!!! CloneObject(): " & ex.Message) 
    End Try 
    Return result 
End Function 
73

Biểu thức biên dịch là cách tốt nhất! (đối với hiệu năng để tạo lặp lại thể hiện trong thời gian chạy).

static readonly Func<X> YCreator = Expression.Lambda<Func<X>>(
    Expression.New(typeof(Y).GetConstructor(Type.EmptyTypes)) 
).Compile(); 

X x = YCreator(); 

Thống kê (2012):

Iterations: 5000000 
    00:00:00.8481762, Activator.CreateInstance(string, string) 
    00:00:00.8416930, Activator.CreateInstance(type) 
    00:00:06.6236752, ConstructorInfo.Invoke 
    00:00:00.1776255, Compiled expression 
    00:00:00.0462197, new 

Thống kê (2015, .net 4.5, x64):

Iterations: 5000000 
    00:00:00.2659981, Activator.CreateInstance(string, string) 
    00:00:00.2603770, Activator.CreateInstance(type) 
    00:00:00.7478936, ConstructorInfo.Invoke 
    00:00:00.0700757, Compiled expression 
    00:00:00.0286710, new 

Thống kê (2015, .net 4.5, x86):

Iterations: 5000000 
    00:00:00.3541501, Activator.CreateInstance(string, string) 
    00:00:00.3686861, Activator.CreateInstance(type) 
    00:00:00.9492354, ConstructorInfo.Invoke 
    00:00:00.0719072, Compiled expression 
    00:00:00.0229387, new 

Thống kê (2017, LINQPad 5.22.02/x64/.NET 4.6):

Iterations: 5000000 
    No args 
    00:00:00.3897563, Activator.CreateInstance(string assemblyName, string typeName) 
    00:00:00.3500748, Activator.CreateInstance(Type type) 
    00:00:01.0100714, ConstructorInfo.Invoke 
    00:00:00.1375767, Compiled expression 
    00:00:00.1337920, Compiled expression (type) 
    00:00:00.0593664, new 
    Single arg 
    00:00:03.9300630, Activator.CreateInstance(Type type) 
    00:00:01.3881770, ConstructorInfo.Invoke 
    00:00:00.1425534, Compiled expression 
    00:00:00.0717409, new 

Full mã:

static X CreateY_New() 
{ 
    return new Y(); 
} 

static X CreateY_New_Arg(int z) 
{ 
    return new Y(z); 
} 

static X CreateY_CreateInstance() 
{ 
    return (X)Activator.CreateInstance(typeof(Y)); 
} 

static X CreateY_CreateInstance_String() 
{ 
    return (X)Activator.CreateInstance("Program", "Y").Unwrap(); 
} 

static X CreateY_CreateInstance_Arg(int z) 
{ 
    return (X)Activator.CreateInstance(typeof(Y), new object[] { z, }); 
} 

private static readonly System.Reflection.ConstructorInfo YConstructor = 
    typeof(Y).GetConstructor(Type.EmptyTypes); 
private static readonly object[] Empty = new object[] { }; 
static X CreateY_Invoke() 
{ 
    return (X)YConstructor.Invoke(Empty); 
} 

private static readonly System.Reflection.ConstructorInfo YConstructor_Arg = 
    typeof(Y).GetConstructor(new[] { typeof(int), }); 
static X CreateY_Invoke_Arg(int z) 
{ 
    return (X)YConstructor_Arg.Invoke(new object[] { z, }); 
} 

private static readonly Func<X> YCreator = Expression.Lambda<Func<X>>(
    Expression.New(typeof(Y).GetConstructor(Type.EmptyTypes)) 
).Compile(); 
static X CreateY_CompiledExpression() 
{ 
    return YCreator(); 
} 

private static readonly Func<X> YCreator_Type = Expression.Lambda<Func<X>>(
    Expression.New(typeof(Y)) 
).Compile(); 
static X CreateY_CompiledExpression_Type() 
{ 
    return YCreator_Type(); 
} 

private static readonly ParameterExpression YCreator_Arg_Param = Expression.Parameter(typeof(int), "z"); 
private static readonly Func<int, X> YCreator_Arg = Expression.Lambda<Func<int, X>>(
    Expression.New(typeof(Y).GetConstructor(new[] { typeof(int), }), new[] { YCreator_Arg_Param, }), 
    YCreator_Arg_Param 
).Compile(); 
static X CreateY_CompiledExpression_Arg(int z) 
{ 
    return YCreator_Arg(z); 
} 

static void Main(string[] args) 
{ 
    const int iterations = 5000000; 

    Console.WriteLine("Iterations: {0}", iterations); 

    Console.WriteLine("No args"); 
    foreach (var creatorInfo in new[] 
    { 
     new {Name = "Activator.CreateInstance(string assemblyName, string typeName)", Creator = (Func<X>)CreateY_CreateInstance}, 
     new {Name = "Activator.CreateInstance(Type type)", Creator = (Func<X>)CreateY_CreateInstance}, 
     new {Name = "ConstructorInfo.Invoke", Creator = (Func<X>)CreateY_Invoke}, 
     new {Name = "Compiled expression", Creator = (Func<X>)CreateY_CompiledExpression}, 
     new {Name = "Compiled expression (type)", Creator = (Func<X>)CreateY_CompiledExpression_Type}, 
     new {Name = "new", Creator = (Func<X>)CreateY_New}, 
    }) 
    { 
     var creator = creatorInfo.Creator; 

     var sum = 0; 
     for (var i = 0; i < 1000; i++) 
      sum += creator().Z; 

     var stopwatch = new Stopwatch(); 
     stopwatch.Start(); 
     for (var i = 0; i < iterations; ++i) 
     { 
      var x = creator(); 
      sum += x.Z; 
     } 
     stopwatch.Stop(); 
     Console.WriteLine("{0}, {1}", stopwatch.Elapsed, creatorInfo.Name); 
    } 

    Console.WriteLine("Single arg"); 
    foreach (var creatorInfo in new[] 
    { 
     new {Name = "Activator.CreateInstance(Type type)", Creator = (Func<int, X>)CreateY_CreateInstance_Arg}, 
     new {Name = "ConstructorInfo.Invoke", Creator = (Func<int, X>)CreateY_Invoke_Arg}, 
     new {Name = "Compiled expression", Creator = (Func<int, X>)CreateY_CompiledExpression_Arg}, 
     new {Name = "new", Creator = (Func<int, X>)CreateY_New_Arg}, 
    }) 
    { 
     var creator = creatorInfo.Creator; 

     var sum = 0; 
     for (var i = 0; i < 1000; i++) 
      sum += creator(i).Z; 

     var stopwatch = new Stopwatch(); 
     stopwatch.Start(); 
     for (var i = 0; i < iterations; ++i) 
     { 
      var x = creator(i); 
      sum += x.Z; 
     } 
     stopwatch.Stop(); 
     Console.WriteLine("{0}, {1}", stopwatch.Elapsed, creatorInfo.Name); 
    } 
} 

public class X 
{ 
    public X() { } 
    public X(int z) { this.Z = z; } 
    public int Z; 
} 

public class Y : X 
{ 
    public Y() {} 
    public Y(int z) : base(z) {} 
} 
+9

+1 cho tất cả các số liệu thống kê! Tôi không thực sự cần loại biểu diễn này vào lúc này, nhưng vẫn rất thú vị. :) – AnorZaken

+0

Cũng có TypeDescriptor.CreateInstance (xem http://stackoverflow.com/a/17797389/1242) mà có thể được nhanh hơn nếu sử dụng với TypeDescriptor.AddProvider –

+1

Đây có phải là vẫn còn hữu ích khi bạn không biết những gì loại 'x' đang ở thời gian chạy? – ajeh

8

Nếu không sử dụng Reflection:

private T Create<T>() where T : class, new() 
{ 
    return new T(); 
} 
+1

Điều này hữu ích như thế nào? Bạn phải biết loại đã gọi phương thức đó, và nếu bạn biết loại bạn có thể xây dựng nó mà không có một phương pháp đặc biệt. –

+0

Vì vậy, T có thể thay đổi theo thời gian chạy. Hữu ích nếu bạn làm việc với các loại bị loại bỏ. –

+0

T mới(); sẽ thất bại nếu T không phải là kiểu tham chiếu với hàm tạo parameterless, Phương thức này sử dụng các contraints để đảm bảo T là kiểu tham chiếu và có một hàm tạo. –

5

Với vấn đề này Activator sẽ làm việc khi có một ctor parameterless. Nếu đây là hạn chế, hãy xem xét sử dụng

System.Runtime.Serialization.FormatterServices.GetSafeUninitializedObject() 
Các vấn đề liên quan