2010-07-30 15 views
3

我是從現有類型創建動態裝配新的類型,但只有選擇的屬性包括:克隆/複製get訪問身體新型

public class EmitTest 
{ 
    public Type Create(Type prototype, Type dynamicBaseType, List<string> includedPropertyList) 
    { 
     AssemblyName aName = new AssemblyName("DynamicAssembly"); 
     AssemblyBuilder assemblyBuilder = AppDomain.CurrentDomain.DefineDynamicAssembly(
       aName, 
       AssemblyBuilderAccess.RunAndSave); 

     ModuleBuilder modulBuilder = assemblyBuilder.DefineDynamicModule(aName.Name, aName.Name + ".dll"); 


     string typeName = string.Concat(prototype.Name, "_DynamicType_", Guid.NewGuid().ToString().Replace("-", string.Empty)); 

     TypeBuilder typeBuilder = modulBuilder.DefineType(
      typeName, 
      TypeAttributes.Public, null, new Type[] { }); 

     foreach (string s in includedPropertyList) 
     { 
      PropertyInfo propertyInfo = prototype.GetProperty(s); 

      if (propertyInfo != null && dynamicBaseType.GetProperty(s) == null) 
      { 
       CreateProperty(typeBuilder, propertyInfo); 
      } 
     } 

     return typeBuilder.CreateType(); 
    } 

    #region Property Creation 

    private void CreateProperty(TypeBuilder typeBuilder, PropertyInfo propertyInfo) 
    { 
     PropertyBuilder propertyBuilder = typeBuilder.DefineProperty(
      propertyInfo.Name, 
      PropertyAttributes.HasDefault, 
      propertyInfo.PropertyType, 
      null); 

     CreatePropertyBase(typeBuilder, propertyBuilder, propertyInfo); 

     AddAttribute<BrowsableAttribute>(propertyBuilder, propertyInfo, CreatePropertyAttributeBrowsable); 
     AddAttribute<DisplayNameAttribute>(propertyBuilder, propertyInfo, CreatePropertyAttributeDisplayName);   
    } 

    private void CreatePropertyBase(TypeBuilder typeBuilder, PropertyBuilder propertyBuilder, PropertyInfo propertyInfo) 
    { 

     FieldBuilder fieldBuilder = typeBuilder.DefineField(
      string.Concat("m_", propertyInfo.Name), 
      propertyInfo.PropertyType, 
      FieldAttributes.Private); 

     MethodAttributes getSetAttr = MethodAttributes.Public | 
      MethodAttributes.SpecialName | MethodAttributes.HideBySig; 


     MethodBuilder mbGetAccessor = typeBuilder.DefineMethod(
      string.Concat("get_", propertyInfo.Name), 
      getSetAttr, 
      propertyInfo.PropertyType, 
      Type.EmptyTypes); 

     ILGenerator mbGetIL = mbGetAccessor.GetILGenerator(); 
     mbGetIL.Emit(OpCodes.Ldarg_0); 
     mbGetIL.Emit(OpCodes.Ldfld, fieldBuilder); 
     mbGetIL.Emit(OpCodes.Ret); 


     MethodBuilder mbSetAccessor = typeBuilder.DefineMethod(
      string.Concat("set_", propertyInfo.Name), 
      getSetAttr, 
      null, 
      new Type[] { propertyInfo.PropertyType }); 

     ILGenerator mbSetIL = mbSetAccessor.GetILGenerator();   
     mbSetIL.Emit(OpCodes.Ldarg_0); 
     mbSetIL.Emit(OpCodes.Ldarg_1); 
     mbSetIL.Emit(OpCodes.Stfld, fieldBuilder); 
     mbSetIL.Emit(OpCodes.Ret); 

     propertyBuilder.SetGetMethod(mbGetAccessor); 
     propertyBuilder.SetSetMethod(mbSetAccessor); 
    } 

    #endregion Property Creation 

    #region Attribute Createion 

    private void AddAttribute<T>(PropertyBuilder propertyBuilder, PropertyInfo propertyInfo, Action<PropertyBuilder, T> action) 
     where T : Attribute 
    { 
     T attribute = ReflectionHelper.GetAttribute(propertyInfo, typeof(T), false) as T; 

     if (attribute != null) 
     { 
      action(propertyBuilder, attribute); 
     } 
    } 

    private void CreatePropertyAttributeBrowsable(PropertyBuilder propertyBuilder, BrowsableAttribute browsableAttribute) 
    { 
     ConstructorInfo myAttrCtor = typeof(BrowsableAttribute).GetConstructor(new Type[] { typeof(bool) }); 
     CustomAttributeBuilder myAttr = new CustomAttributeBuilder(myAttrCtor, new object[] { browsableAttribute.Browsable }); 
     propertyBuilder.SetCustomAttribute(myAttr); 
    } 

    private void CreatePropertyAttributeDisplayName(PropertyBuilder propertyBuilder, DisplayNameAttribute displayNameAttribute) 
    { 
     ConstructorInfo myAttrCtor2 = typeof(DisplayNameAttribute).GetConstructor(new Type[] { typeof(string) }); 
     CustomAttributeBuilder myAttr2 = new CustomAttributeBuilder(myAttrCtor2, new object[] { displayNameAttribute.DisplayName }); 
     propertyBuilder.SetCustomAttribute(myAttr2); 
    } 

    #endregion Attribute Createion 
} 

這一切的偉大工程,但在這裏我只創建獲取或設置私有字段的簡單屬性。

我遇到了更復雜屬性的情況,例如在getter中有類似「A + B * C」的地方,其中A,B和C是屬於同一類的屬性。

我試圖像這樣創造CreatePropertyBase getter方法的副本:

 MethodBuilder mbNumberGetAccessor = typeBuilder.DefineMethod(
      string.Concat("get_", propertyInfo.Name), 
      getSetAttr, 
      propertyInfo.PropertyType, 
      Type.EmptyTypes);  


     System.Reflection.MethodInfo mi = propertyInfo.GetGetMethod(); 
     byte[] body = mi.GetMethodBody().GetILAsByteArray(); 

     mbNumberGetAccessor.CreateMethodBody(body, body.Length); 

這顯然沒有奏效。 :)

我的問題是:是否有可能複製身體的get訪問器,引用同一類中的其他屬性,並且如果可能的話該怎麼做。 我正在使用.NET 3.5 SP1

謝謝。

回答

0

不,您必須從源屬性動態反編譯IL,因爲源屬性中的IL將具有對其引用的其他屬性的靜態PropertyInfo引用。而那些引用將是原始的PropertyInfo而不是你最近創建的,這可能是爲什麼它給你錯誤。

出於好奇,你不只是使用匿名類型做同樣的事情的原因是什麼?

+0

我有文字,包含屬性名稱的字符串數組的工作,我必須將屬性添加到那些屬性等我看不到我怎麼能用所有的匿名類型,或者我可以嗎? :) – 2010-07-30 14:30:03

0

你可以使用這個幫手(CIL Reader)有必要的修改,以遍歷屬性訪問的機構和重建他們