2010-01-12 15 views
6

Tôi đã tìm thấy this nhưng đã cố gắng sử dụng nó và không thành công.Sử dụng một đại biểu để gọi một nhà xây dựng

Làm cách nào tôi có thể tạo đối tượng bằng cách sử dụng phản xạ và làm cho nó nhanh bằng cách đặt nó vào đại biểu?

 DynamicMethod dm = new DynamicMethod("MyCtor", t, new Type[] { });    
     var ctor = t.GetConstructor(new Type[] { }); 
     ILGenerator ilgen = dm.GetILGenerator(); 
     ilgen.Emit(OpCodes.Ldarg_0); 
     ilgen.Emit(OpCodes.Newobj, ctor); 
     ilgen.Emit(OpCodes.Ret); 
     var d = (Func<T>)dm.CreateDelegate(t); 
     dm.Invoke(null, new object[] { }); 

Trước khi đặt nó trong một deleage tôi đã cố gắng để ít nhất gọi nó và khi tôi đã làm ở trên tôi nhận được lỗi

An unhandled exception of type 'System.Reflection.TargetInvocationException' occurred in mscorlib.dll 

Thông tin thêm: ngoại lệ đã được ném bởi mục tiêu của một sự thỉnh nguyện.

Nếu tôi gọi d() thay vì tôi nhận được ngoại trừ

An unhandled exception of type 'System.ArgumentException' occurred in mscorlib.dll 

Additional information: Type must derive from Delegate. 

Làm thế nào để tôi đặt một không constructor param thành một đại biểu và gọi nó?

+2

Bạn gặp sự cố gì với Activator.CreateInstance? – dsolimano

+0

dsolimano: Để làm chậm. Tôi đang tạo ra hàng nghìn đồ vật và nhiều thứ nữa. –

Trả lời

11

Nếu bạn có quyền truy cập vào NET 3.5 (mà bạn sử dụng Func<T> gợi ý), bạn có thể tìm thấy Expression dễ dàng hơn ILGenerator:

class Foo { } 
static void Main() { 
    Func<Foo> func = GetCtor<Foo>(); // cache this somewhere! 
    Foo foo = func(); 
} 
static Func<T> GetCtor<T>() { 
    Type type = typeof(T); 
    Expression body = Expression.New(type); 
    return Expression.Lambda<Func<T>>(body).Compile();   
} 

Khá dễ dàng để mở rộng để sử dụng một hàm tạo cụ thể, chuyển các đối số, hoặc thêm các ràng buộc thuộc tính sau hàm tạo; phôi, chuyển đổi, v.v ... (xem this related answer). Nếu bạn có một kịch bản cụ thể, tôi sẽ vui vẻ thêm một ví dụ.

Cũng lưu ý rằng bạn nên lưu và sử dụng lại bất kỳ nhà thầu nào như vậy - nếu không bạn sẽ mất lợi ích (tức là không tạo lại ủy quyền cho mỗi cuộc gọi).

+0

Câu trả lời tuyệt vời như mọi khi. –

3

Hãy thử điều này -

Action myCtor = CreateCtor(t, Type.EmptyTypes, typeof(Action)); 

public static Delegate CreateCtor(Type type, Type[] parameterTypes, Type delegateType, string typeParameterName) 
{ 
    var ctorInfo = type.GetConstructor(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance, null, parameterTypes, null); 
    if (ctorInfo == null) 
    { 
     string parameterString = string.Empty; 
     if(parameterTypes.Length > 0) 
     { 
      string[] parameterStrings = new string[parameterTypes.Length]; 
      for(int i = 0; i < parameterTypes.Length; ++i) 
      { 
       parameterStrings[i] = parameterTypes[i].ToString(); 
      } 
      parameterString = string.Join(",", parameterStrings); 
     } 
     throw new ArgumentException(string.Format("Type '{0}' does not define .ctor({1}).", type, parameterString), typeParameterName); 
    } 

    bool isVisible = type.IsVisible && (ctorInfo.IsPublic && !ctorInfo.IsFamilyOrAssembly); 

    DynamicMethod dynamicCtor = new DynamicMethod(Guid.NewGuid().ToString("N"), type, parameterTypes, ctorInfo.Module, !isVisible); 
    var il = dynamicCtor.GetILGenerator(); 
    for (int i = 0; i < parameterTypes.Length; ++i) 
    { 
     switch (i) 
     { 
      case 0: il.Emit(OpCodes.Ldarg_0); break; 
      case 1: il.Emit(OpCodes.Ldarg_1); break; 
      case 2: il.Emit(OpCodes.Ldarg_2); break; 
      case 3: il.Emit(OpCodes.Ldarg_3); break; 
      default: il.Emit(OpCodes.Ldarg, i); break; 
     } 
    } 
    il.Emit(OpCodes.Newobj, ctorInfo); 
    il.Emit(OpCodes.Ret); 
    return dynamicCtor.CreateDelegate(delegateType); 
} 
0

Không có đối số cho các nhà xây dựng, do đó bạn không nên tải lập luận trên stack ilgen.Emit(OpCodes.Ldarg_0):

class Program 
{ 
    static void Main() 
    { 
     var t = typeof(Program); 
     var dm = new DynamicMethod("MyCtor", t, new Type[0], t.Module); 
     var ctor = t.GetConstructor(new Type[0]); 
     ILGenerator ilgen = dm.GetILGenerator(); 
     ilgen.Emit(OpCodes.Newobj, ctor); 
     ilgen.Emit(OpCodes.Ret); 
     var del = (Func<Program>)dm.CreateDelegate(typeof(Func<Program>)); 
     var instance = del(); 
     Console.WriteLine(instance); 
    } 
} 
0

Phương pháp chung để xây dựng đại biểu, gọi hàm tạo trực tiếp. Tự động tìm kiếm hàm tạo trong loại đã cho với chữ ký của loại đại biểu đã cho và cấu trúc ủy nhiệm của loại đó. Mã tại đây:

/// <summary> 
/// Reflective object construction helper. 
/// All methods are thread safe. 
/// </summary> 
public static class Constructor 
{ 
    /// <summary> 
    /// Searches an instanceType constructor with delegateType-matching signature and constructs delegate of delegateType creating new instance of instanceType. 
    /// Instance is casted to delegateTypes's return type. 
    /// Delegate's return type must be assignable from instanceType. 
    /// </summary> 
    /// <param name="delegateType">Type of delegate, with constructor-corresponding signature to be constructed.</param> 
    /// <param name="instanceType">Type of instance to be constructed.</param> 
    /// <returns>Delegate of delegateType wich constructs instance of instanceType by calling corresponding instanceType constructor.</returns> 
    public static Delegate Compile(Type delegateType,Type instanceType) 
    { 
     if (!typeof(Delegate).IsAssignableFrom(delegateType)) 
     { 
      throw new ArgumentException(String.Format("{0} is not a Delegate type.",delegateType.FullName),"delegateType"); 
     } 
     var invoke = delegateType.GetMethod("Invoke"); 
     var parameterTypes = invoke.GetParameters().Select(pi => pi.ParameterType).ToArray(); 
     var resultType = invoke.ReturnType; 
     if(!resultType.IsAssignableFrom(instanceType)) 
     { 
      throw new ArgumentException(String.Format("Delegate's return type ({0}) is not assignable from {1}.",resultType.FullName,instanceType.FullName)); 
     } 
     var ctor = instanceType.GetConstructor(
      BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, parameterTypes, null); 
     if(ctor == null) 
     { 
      throw new ArgumentException("Can't find constructor with delegate's signature","instanceType"); 
     } 
     var parapeters = parameterTypes.Select(Expression.Parameter).ToArray(); 

     var newExpression = Expression.Lambda(delegateType, 
      Expression.Convert(Expression.New(ctor, parapeters), resultType), 
      parapeters); 
     var @delegate = newExpression.Compile(); 
     return @delegate; 
    } 
    public static TDelegate Compile<TDelegate>(Type instanceType) 
    { 
     return (TDelegate) (object) Compile(typeof (TDelegate), instanceType); 
    } 
} 

là một phần của nguồn dự án Yappi. Sử dụng nó, bạn có thể xây dựng delegate gọi bất kỳ hàm tạo nào của kiểu đã cho, bao gồm cả hàm tạo với các tham số (ngoại trừ các tham số ref và out).

sử dụng mẫu:

var newList = Constructor.Compile<Func<int, IList<String>>>(typeof (List<String>)); 
var list = newList(100); 

Sau khi xây dựng các đại biểu, lưu trữ nó ở đâu đó trong từ điển tĩnh hoặc trong lĩnh vực tĩnh của lớp học với tham số chung. Không xây dựng đại biểu mới mỗi lần. Sử dụng một đại biểu để xây dựng nhiều phiên bản của loại đã cho.