Câu hỏi rất cũ, nhưng câu trả lời mới ;-)
Phiên bản ExpressionTree : (Tôi nghĩ giải pháp nhanh nhất và sạch nhất)
Giống như Welly Tambunan nói, "chúng ta cũng có thể sử dụng cây biểu thức để xây dựng đối tượng"
Điều này sẽ tạo ra một 'hàm tạo' (hàm) cho loại / tham số đã cho. Nó trả về một đại biểu và chấp nhận các kiểu tham số dưới dạng một mảng các đối tượng.
Đây là:
// this delegate is just, so you don't have to pass an object array. _(params)_
public delegate object ConstructorDelegate(params object[] args);
public static ConstructorDelegate CreateConstructor(Type type, params Type[] parameters)
{
// Get the constructor info for these parameters
var constructorInfo = type.GetConstructor(parameters);
// define a object[] parameter
var paramExpr = Expression.Parameter(typeof(Object[]));
// To feed the constructor with the right parameters, we need to generate an array
// of parameters that will be read from the initialize object array argument.
var constructorParameters = parameters.Select((paramType, index) =>
// convert the object[index] to the right constructor parameter type.
Expression.Convert(
// read a value from the object[index]
Expression.ArrayAccess(
paramExpr,
Expression.Constant(index)),
paramType)).ToArray();
// just call the constructor.
var body = Expression.New(constructorInfo, constructorParameters);
var constructor = Expression.Lambda<ConstructorDelegate>(body, paramExpr);
return constructor.Compile();
}
Ví dụ MyClass:
public class MyClass
{
public int TestInt { get; private set; }
public string TestString { get; private set; }
public MyClass(int testInt, string testString)
{
TestInt = testInt;
TestString = testString;
}
}
Sử dụng:
// you should cache this 'constructor'
var myConstructor = CreateConstructor(typeof(MyClass), typeof(int), typeof(string));
// Call the `myConstructor` function to create a new instance.
var myObject = myConstructor(10, "test message");
Một ví dụ khác: truyền các kiểu dưới dạng một mảng
var type = typeof(MyClass);
var args = new Type[] { typeof(int), typeof(string) };
// you should cache this 'constructor'
var myConstructor = CreateConstructor(type, args);
// Call the `myConstructor` fucntion to create a new instance.
var myObject = myConstructor(10, "test message");
Gỡ lỗi biểu hiện
.Lambda #Lambda1<TestExpressionConstructor.MainWindow+ConstructorDelegate>(System.Object[] $var1) {
.New TestExpressionConstructor.MainWindow+MyClass(
(System.Int32)$var1[0],
(System.String)$var1[1])
}
Điều này tương đương với mã được tạo ra:
public object myConstructor(object[] var1)
{
return new MyClass(
(System.Int32)var1[0],
(System.String)var1[1]);
}
Nhược điểm nhỏ
Tất cả các tham số valuetypes được đóng hộp khi chúng được truyền như một mảng đối tượng.
Kiểm tra hiệu suất đơn giản:
private void TestActivator()
{
Stopwatch sw = Stopwatch.StartNew();
for (int i = 0; i < 1024 * 1024 * 10; i++)
{
var myObject = Activator.CreateInstance(typeof(MyClass), 10, "test message");
}
sw.Stop();
Trace.WriteLine("Activator: " + sw.Elapsed);
}
private void TestReflection()
{
var constructorInfo = typeof(MyClass).GetConstructor(new[] { typeof(int), typeof(string) });
Stopwatch sw = Stopwatch.StartNew();
for (int i = 0; i < 1024 * 1024 * 10; i++)
{
var myObject = constructorInfo.Invoke(new object[] { 10, "test message" });
}
sw.Stop();
Trace.WriteLine("Reflection: " + sw.Elapsed);
}
private void TestExpression()
{
var myConstructor = CreateConstructor(typeof(MyClass), typeof(int), typeof(string));
Stopwatch sw = Stopwatch.StartNew();
for (int i = 0; i < 1024 * 1024 * 10; i++)
{
var myObject = myConstructor(10, "test message");
}
sw.Stop();
Trace.WriteLine("Expression: " + sw.Elapsed);
}
TestActivator();
TestReflection();
TestExpression();
Các kết quả:
Activator: 00:00:13.8210732
Reflection: 00:00:05.2986945
Expression: 00:00:00.6681696
Sử dụng nhanh hơnExpressions
+/- 8 lần so với Gọi ConstructorInfo
và nhanh hơn +/- 20 lần so với sử dụngActivator