将 get 访问器主体克隆/复制为新类型

发布于 2024-09-12 07:42:46 字数 5340 浏览 3 评论 0原文

我正在从现有类型的动态程序集中创建新类型,但仅包含选定的属性:

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

谢谢。

I'm creating new type in dynamic assembly from existing type, but with only selected properties to include:

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
}

This all works great, but here I'm creating only simple properties that just get or set private field.

I ran into cases where I have more complex property, for example in getter there is something like: "A + B * C" where A,B and C are properties in same class.

I tried creating copy of getter in CreatePropertyBase method like this:

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

This obviously didn't work. :)

My question is: Is it possible to copy body of get accessor that has references to other properties in same class, and if possible how to do it.
I'm using .NET 3.5 SP1

Thanks.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

一袭水袖舞倾城 2024-09-19 07:42:46

不,您必须从源属性动态反编译 IL,因为源属性中的 IL 将具有对其所引用的其他属性的静态 PropertyInfo 引用。这些引用将指向原始的 PropertyInfo 而不是您新创建的 PropertyInfo,这可能就是它给您带来错误的原因。

出于好奇,您不只使用匿名类型来做同样的事情的原因是什么?

No, you'll have to dynamically decompile the IL from the source property because the IL in the source property would have static PropertyInfo references to the other properties it is referencing. And those references would be to the original PropertyInfo and not to your newly created ones, which is probably why it gave you errors.

Out of curiosity, what's the reason you don't just use an anonymous type to do the same thing?

爱格式化 2024-09-19 07:42:46

您可以使用此帮助程序(CIL Reader ) 遍历属性访问器的主体并通过必要的修改重建它们

You can use this helper (CIL Reader) to traverse bodies of property accessors and reconstruct them with necessary modifications

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文