2011-07-29 12 views
13

कहो मैं एक SpaceShip वर्ग है, इसलिए की तरह के लिए एक पास के माध्यम से निर्माता बनाने के लिए TypeBuilder का उपयोग करना:आधार वर्ग

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

मैं TypeBuilder उपयोग करने के लिए रन-टाइम जो अंतरिक्ष यान से विरासत में एक प्रकार का निर्माण करना चाहते , और स्पेसशिप में से प्रत्येक के लिए एक कन्स्ट्रक्टर को परिभाषित करता है। मुझे वास्तव में करने के लिए रचनाकारों की आवश्यकता नहीं है माता-पिता ("पास-थ्रू" कन्स्ट्रक्टर) तक उनके तर्कों को छोड़कर कुछ भी। उदाहरण के लिए, उत्पन्न प्रकार कुछ इस तरह दिखाई होता अगर सी # में व्यक्त किया:

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

चीज़ें थोड़ी जटिल करने के लिए, मैं वास्तव में नहीं जानता कि कौन सी क्लास उत्पन्न प्रकार चलाने के समय जब तक से इनहेरिट किया जाएगा (ताकि मुझे किसी भी संख्या में रचनाकारों को ध्यान में रखना होगा, संभवतः डिफ़ॉल्ट पैरामीटर के साथ)।

क्या यह संभव है? मुझे लगता है कि अगर मैं शुरू करने के लिए एक सामान्य दिशा थी, तो मैं इसे समझ सकता हूं, यह सिर्फ इतना है कि मैं TypeBuilder पर पूरी तरह से नया हूं।

धन्यवाद!

उत्तर

19

ठीक है, मुझे ऑनलाइन कुछ भी नहीं मिला, इसलिए मैंने अपना खुद का कार्यान्वयन समाप्त कर दिया। इससे किसी भी प्रकार की प्रॉक्सी लिखने में भी मदद मिलनी चाहिए।

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

प्रयोग (यह मानते हुए कि आप एक TypeBuilder वस्तु है - एक उदाहरण के लिए here देखें):

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

मैं के लिए बस क्या देख रहा था! हालांकि मुझे उन पक्की 'संरक्षित' कन्स्ट्रक्टर प्राप्त करने के लिए 'GetConstructors (BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance)' में बदलना पड़ा था। – dlras2

+0

@Dan: बढ़िया, मुझे यकीन नहीं था कि किसी और को इसकी आवश्यकता होगी इस। उस कीवर्ड को जोड़ने के लिए धन्यवाद, मेरे पास हाथ से घोषणा लिखने के लिए (गलत) होना चाहिए, फिर केवल शरीर को प्रतिलिपि बनाना चाहिए। – Cameron

+0

यह महोदय, शुद्ध सोने है! – Waescher

संबंधित मुद्दे