Có thể không phải lúc nào cũng biết Loại đối tượng tại thời gian biên dịch, nhưng có thể cần phải tạo một thể hiện Kiểu. Làm thế nào để bạn có được một thể hiện đối tượng mới từ một loại?Nhận một cá thể đối tượng mới từ một loại
Trả lời
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");
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);
}
}
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
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.
"Liên kết mã vạch IL và DynamicMethod" đã chết. –
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>();
Ngoại trừ việc này không hoạt động trong thời gian chạy 'Loại t'. –
@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ó. –
@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
Dường như khu chung T t = new T();
làm việc?
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. –
public AbstractType New
{
get
{
return (AbstractType) Activator.CreateInstance(GetType());
}
}
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 }
);
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");
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
@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 :) –
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
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) {}
}
+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
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 –
Đâ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
Nếu không sử dụng Reflection:
private T Create<T>() where T : class, new()
{
return new T();
}
Đ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. –
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ỏ. –
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. –
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()
- 1. Nhận cá thể của đối tượng con trong iframe
- 2. Làm thế nào để lấy một đối tượng ngẫu nhiên từ một cá thể NSSet?
- 3. Tạo một cá thể, tôi đã có loại
- 4. Sự khác biệt chức năng giữa một cá thể và một đối tượng là gì?
- 5. Làm thế nào tôi có thể nhận được một đối tượng HDC từ một đối tượng CDC?
- 6. Nhận một đối tượng ngày (trước sáu tháng) từ một đối tượng ngày
- 7. Khởi tạo một đối tượng mới từ lớp trong Enum
- 8. Các cuộc gọi tiếp theo đến kết quả Mock.Setup trong cùng một cá thể đối tượng
- 9. bằng cách sử dụng django, làm thế nào để tôi xây dựng một thể hiện đối tượng proxy từ một cá thể đối tượng superclass?
- 10. Làm thế nào để một lớp trừu tượng "DocumentBuilderFactory" được phép tạo nhanh cá thể mới
- 11. Sự khác biệt giữa một cá thể và một đối tượng là gì?
- 12. Có cách nào đúng để trả về một cá thể đối tượng mới bằng cách tham chiếu trong C++ không?
- 13. giá trị loại bỏ mặc định từ một đối tượng
- 14. Nhận giá trị từ một đối tượng trong JavaScript
- 15. Nhận giá trị từ một đối tượng động động
- 16. Phát sinh một đối tượng HTMLElement từ đối tượng jQuery
- 17. Nhận tên sheet từ một tên dãy Tên đối tượng
- 18. Ngăn ActiveRecord lưu() trên một cá thể
- 19. Nhận đối tượng cụ thể từ các rdata nộp
- 20. Nhận danh sách các cá thể trong một loại lớp trong Haskell
- 21. đối tượng Insert mới với đối tượng
- 22. Python: cách chia sẻ một cá thể đối tượng trên nhiều lời gọi của một tập lệnh
- 23. Làm cách nào để tạo một cá thể GzipFile từ "đối tượng giống như tệp" mà urllib.urlopen() trả về?
- 24. Cách trả về một cá thể mới của bản thân từ một phương thức thể hiện ruby
- 25. Phân loại một danh sách mảng các đối tượng dựa trên một biến trong đối tượng
- 26. Tại sao một số lớp không cần từ "Mới" khi tạo cá thể?
- 27. Python ElementTree: Phân tích cú pháp một chuỗi và nhận cá thể ElementTree
- 28. động tạo ra một lớp javascript mới từ một đối tượng hiện
- 29. Có một call_user_func() tương đương để tạo một cá thể lớp mới không?
- 30. Lấy tên của lớp từ một cá thể
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
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(); –