2011-07-29 25 views
13

Nói rằng tôi có một lớp SpaceShip, như vậy:Sử dụng TypeBuilder để tạo ra một constructor pass-through cho lớp cơ sở

public class SpaceShip { 
    public SpaceShip() { } 
    public SpaceShip(IRocketFuelSource fuelSource) { } 
} 

Tôi muốn sử dụng TypeBuilder để tạo ra một loại tại thời gian chạy mà được thừa hưởng từ tàu vũ trụ và định nghĩa một hàm tạo cho mỗi cái trong SpaceShip. Tôi không cần các nhà xây dựng để thực sự làm bất cứ điều gì ngoại trừ vượt qua đối số của họ lên đến cha mẹ ("pass-through" constructors). Ví dụ, loại được tạo ra sẽ giống như thế này nếu thể hiện bằng C#:

public class SpaceShipSubClass : SpaceShip { 
    public SpaceShipSubClass() : base() { } 
    public SpaceShipSubClass(IRocketFuelSource fuelSource) : base(fuelSource) { } 
} 

Để những điều phức tạp một chút, tôi không thực sự biết được lớp học kiểu được tạo ra sẽ được kế thừa từ cho đến khi thời gian chạy (vì vậy Tôi sẽ phải đưa vào tài khoản bất kỳ số lượng các nhà xây dựng, có thể với các tham số mặc định).

Điều này có khả thi không? Tôi nghĩ rằng tôi có thể tìm ra nếu tôi có một hướng chung để bắt đầu, nó chỉ là tôi hoàn toàn mới với TypeBuilder.

Cảm ơn!

Trả lời

19

Được rồi, tôi không thể tìm thấy bất kỳ điều gì trực tuyến, vì vậy tôi đã kết thúc việc triển khai của riêng mình. Điều này sẽ giúp bắt đầu bất cứ ai viết một số loại proxy, quá.

public static class TypeBuilderHelper 
{ 
    /// <summary>Creates one constructor for each public constructor in the base class. Each constructor simply 
    /// forwards its arguments to the base constructor, and matches the base constructor's signature. 
    /// Supports optional values, and custom attributes on constructors and parameters. 
    /// Does not support n-ary (variadic) constructors</summary> 
    public static void CreatePassThroughConstructors(this TypeBuilder builder, Type baseType) 
    { 
     foreach (var constructor in baseType.GetConstructors()) { 
      var parameters = constructor.GetParameters(); 
      if (parameters.Length > 0 && parameters.Last().IsDefined(typeof(ParamArrayAttribute), false)) { 
       //throw new InvalidOperationException("Variadic constructors are not supported"); 
       continue; 
      } 

      var parameterTypes = parameters.Select(p => p.ParameterType).ToArray(); 
      var requiredCustomModifiers = parameters.Select(p => p.GetRequiredCustomModifiers()).ToArray(); 
      var optionalCustomModifiers = parameters.Select(p => p.GetOptionalCustomModifiers()).ToArray(); 

      var ctor = builder.DefineConstructor(MethodAttributes.Public, constructor.CallingConvention, parameterTypes, requiredCustomModifiers, optionalCustomModifiers); 
      for (var i = 0; i < parameters.Length; ++i) { 
       var parameter = parameters[i]; 
       var parameterBuilder = ctor.DefineParameter(i + 1, parameter.Attributes, parameter.Name); 
       if (((int)parameter.Attributes & (int)ParameterAttributes.HasDefault) != 0) { 
        parameterBuilder.SetConstant(parameter.RawDefaultValue); 
       } 

       foreach (var attribute in BuildCustomAttributes(parameter.GetCustomAttributesData())) { 
        parameterBuilder.SetCustomAttribute(attribute); 
       } 
      } 

      foreach (var attribute in BuildCustomAttributes(constructor.GetCustomAttributesData())) { 
       ctor.SetCustomAttribute(attribute); 
      } 

      var emitter = ctor.GetILGenerator(); 
      emitter.Emit(OpCodes.Nop); 

      // Load `this` and call base constructor with arguments 
      emitter.Emit(OpCodes.Ldarg_0); 
      for (var i = 1; i <= parameters.Length; ++i) { 
       emitter.Emit(OpCodes.Ldarg, i); 
      } 
      emitter.Emit(OpCodes.Call, constructor); 

      emitter.Emit(OpCodes.Ret); 
     } 
    } 


    private static CustomAttributeBuilder[] BuildCustomAttributes(IEnumerable<CustomAttributeData> customAttributes) 
    { 
     return customAttributes.Select(attribute => { 
      var attributeArgs = attribute.ConstructorArguments.Select(a => a.Value).ToArray(); 
      var namedPropertyInfos = attribute.NamedArguments.Select(a => a.MemberInfo).OfType<PropertyInfo>().ToArray(); 
      var namedPropertyValues = attribute.NamedArguments.Where(a => a.MemberInfo is PropertyInfo).Select(a => a.TypedValue.Value).ToArray(); 
      var namedFieldInfos = attribute.NamedArguments.Select(a => a.MemberInfo).OfType<FieldInfo>().ToArray(); 
      var namedFieldValues = attribute.NamedArguments.Where(a => a.MemberInfo is FieldInfo).Select(a => a.TypedValue.Value).ToArray(); 
      return new CustomAttributeBuilder(attribute.Constructor, attributeArgs, namedPropertyInfos, namedPropertyValues, namedFieldInfos, namedFieldValues); 
     }).ToArray(); 
    } 
} 

sử dụng (giả sử bạn có một đối tượng TypeBuilder - xem here cho một ví dụ):

var typeBuilder = ...; // TypeBuilder for a SpaceShipSubClass 
typeBuilder.CreatePassThroughConstructors(typeof(SpaceShip)); 
var subType = typeBuilder.CreateType(); // Woo-hoo, proxy constructors! 
+2

Chỉ cần những gì tôi đang tìm kiếm! Mặc dù tôi đã phải thay đổi thành 'GetConstructors (BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance)' để có được những nhà xây dựng được bảo vệ 'pesky '.. – dlras2

+0

@Dan: Tuyệt vời, tôi không chắc ai khác có nhu cầu điều này. Cảm ơn bạn đã thêm từ khóa đó, tôi phải có (mis) viết tờ khai bằng tay sau đó sao chép chỉ dán nội dung. – Cameron

+0

Thưa ông, là vàng nguyên chất! – Waescher

Các vấn đề liên quan