使用Emit动态创建类后设置属性值失败

pig357 2017-03-13 04:22:55
环境:Framework4.0 C# Emit
问题:从配置文件中获取数据结构,使用Emit动态创建了类,然后为属性赋值时出错,提示“”无法绑定到目标方法,因其签名或安全透明度与委托类型的签名或安全透明度不兼容。“”


EmitHelper emit = new EmitHelper();
emit.CreateType("DynamicAssembly", "Student2");
emit.CreateProperty("Name", typeof(string));
emit.Save();
object ee = emit.CreateInstance();
emit.Set(ee, "Name", "sdasdas");
s = emit.Get(ee, "Name").ToString();

//帮助
public class EmitHelper
{
private TypeBuilder _typeBuilder = null;
private AssemblyBuilder _assemblyBuilder = null;
public Type ThisType { get; set; }

public delegate void SetPropertyValue(object obj, object value);
public delegate object GetPropertyValue(object obj);
private Dictionary<string, SetPropertyValue> _dicSetProperty = new Dictionary<string, SetPropertyValue>();
private Dictionary<string, GetPropertyValue> _dicGetProperty = new Dictionary<string, GetPropertyValue>();
private Dictionary<string, PropertyInfo> _dicProperty = new Dictionary<string, PropertyInfo>();

public void CreateType(string assembly, string type)
{
AssemblyName DemoName = new AssemblyName(assembly);
_assemblyBuilder = AppDomain.CurrentDomain.DefineDynamicAssembly(DemoName, AssemblyBuilderAccess.RunAndSave);
ModuleBuilder mb = _assemblyBuilder.DefineDynamicModule(DemoName.Name, DemoName.Name + ".dll");
_typeBuilder = mb.DefineType(type, TypeAttributes.Public);
}

public void CreateProperty(string propertyName, Type propertyType)
{
TypeBuilder tb = _typeBuilder;
var field = tb.DefineField("_" + propertyName, propertyType, FieldAttributes.Private);
var getMethod = tb.DefineMethod("get_" + propertyName, MethodAttributes.Public, propertyType, null);
var setMethod = tb.DefineMethod("set_" + propertyName, MethodAttributes.Public, null, new Type[] { propertyType });
var ilGet = getMethod.GetILGenerator();
ilGet.Emit(OpCodes.Ldarg_0);
ilGet.Emit(OpCodes.Ldfld, field);
ilGet.Emit(OpCodes.Ret);

var ilSet = setMethod.GetILGenerator();
ilSet.Emit(OpCodes.Ldarg_0);
ilSet.Emit(OpCodes.Ldarg_1);
ilSet.Emit(OpCodes.Stfld, field);
ilSet.Emit(OpCodes.Ret);

var property = tb.DefineProperty(propertyName, PropertyAttributes.None, propertyType, null);
property.SetGetMethod(getMethod);
property.SetSetMethod(setMethod);

Save();

var callMethod = ThisType.GetMethod("set_" + propertyName, BindingFlags.Instance | BindingFlags.IgnoreCase | BindingFlags.Public | BindingFlags.NonPublic);
var para = callMethod.GetParameters()[0];
//// 创建动态函数
DynamicMethod method = new DynamicMethod("EmitCallable", null, new Type[] { ThisType, propertyType }, ThisType);
// 获取动态函数的 IL 生成器
var il = method.GetILGenerator();
// 创建一个本地变量,主要用于 Object Type to Propety Type
var local = il.DeclareLocal(para.ParameterType, true);
// 加载第 2 个参数【(T owner, object value)】的 value
il.Emit(OpCodes.Ldarg_1);
if (para.ParameterType.IsValueType)
{
il.Emit(OpCodes.Unbox_Any, para.ParameterType);// 如果是值类型,拆箱 string = (string)object;
}
else
{
il.Emit(OpCodes.Castclass, para.ParameterType);// 如果是引用类型,转换 Class = object as Class
}
il.Emit(OpCodes.Stloc, local);// 将上面的拆箱或转换,赋值到本地变量,现在这个本地变量是一个与目标函数相同数据类型的字段了。
il.Emit(OpCodes.Ldarg_0); // 加载第一个参数 owner
il.Emit(OpCodes.Ldloc, local);// 加载本地参数
il.EmitCall(OpCodes.Callvirt, callMethod, null);//调用函数
il.Emit(OpCodes.Ret); // 返回
_dicSetProperty[propertyName] = method.CreateDelegate(typeof(SetPropertyValue)) as SetPropertyValue;

var dm = new DynamicMethod(name: "EmittedGetter", returnType: typeof(string), parameterTypes: new[] { ThisType }, owner: ThisType);

var type = ThisType;
var prop = type.GetMethod("get_Name");
var il = dm.GetILGenerator();
il.Emit(OpCodes.Ldarg_0);
il.Emit(OpCodes.Call, prop);
il.Emit(OpCodes.Ret);
_dicGetProperty[propertyName] = dm.CreateDelegate(typeof(GetPropertyValue)) as GetPropertyValue;
}

public Type Save()
{
ThisType = _typeBuilder.CreateType();
return ThisType;
}

public object CreateInstance()
{
return Activator.CreateInstance(ThisType);
}

public void Set(object obj, string propertyName, object value)
{
_dicSetProperty[propertyName](obj, value);
}

public object Get(object obj, string propertyName)
{
return _dicGetProperty[propertyName](obj);
}
}

在CreateDelegate(typeof(GetPropertyValue)) as GetPropertyValue;这里报错,提示“”“无法绑定到目标方法,因其签名或安全透明度与委托类型的签名或安全透明度不兼容。”
创建类是正确的,通过反射可以看到创建类的源码:

使用反射获取或设置属性没有问题,但使用CreateDelegate的方法就报错。请问这是什么原因呢,怎么修改?
...全文
305 14 打赏 收藏 转发到动态 举报
写回复
用AI写文章
14 条回复
切换为时间正序
请发表友善的回复…
发表回复
Poopaye 2017-03-15
  • 打赏
  • 举报
回复
直接赋值肯定最快了:
class A:IA
{
    public string Name{get;set;}
    public int Age{get;set;}
}
如果一定要用方法来赋值,也是这样:
class A:IA
{
    string Name;
    int Age;
    public void Set(int property, object value){
        switch(property){
             case 0: Name = value; break;
             ......
        }
   }
}
能分类型更好了:
    public void SetInt(int property, int value){
        switch(property){
             case 0: Age = value; break;
             ......
        }
   }
pig357 2017-03-15
  • 打赏
  • 举报
回复
引用 12 楼 shingoscar 的回复:
就是这个意思,不过你这样设置属性 [quote=引用 11 楼 pig357 的回复:] public void Set(string propertyName,object value){ if(propertyName=="Name ") Name = value; else if(propertyName=="Age") Age= value; }
还不如用Dictionary、反射、或者dynamic呢 完全没体现出有用动态类的必要[/quote] 你好,我这边想的是使用emit效率比反射和Dynamic高一些,毕竟实际使用中数据量比较大吧。 请问你有什么更好的方法或者优化方案么?
Poopaye 2017-03-14
  • 打赏
  • 举报
回复
引用 5 楼 pig357 的回复:
请问你有什么好的办法么?
方法在4楼 顺便说下,如果有抽象类,那也用不着什么delegate,直接调用好了 实现接口也可以
pig357 2017-03-14
  • 打赏
  • 举报
回复
引用 3 楼 shingoscar 的回复:
[quote=引用 2 楼 pig357 的回复:] 你好,我把委托类型改为string
楼主这智力不太适合编程啊 你只是把返回类型改成了string,但是参数类型还是object,我简写为string(object) 而你创建的方法签名是:returnType: typeof(string), parameterTypes: new[] { ThisType },简写为string(ThisType) 虽然我不知道ThisType具体是哪个类型,但看名字肯定不是object [/quote] 恩,我晓得问题所在了,ThisType是emit动态创建的类,之前考虑使用泛型委托,但动态创建的ThisType不能使用。

//创建ThisType
emit.CreateType("DynamicAssembly", "Student2");
            emit.CreateProperty("Name", typeof(string));

//定义委托
public delegate void SetPropertyValue<T>(T obj, string value);
请问你有什么好的办法么?
Poopaye 2017-03-14
  • 打赏
  • 举报
回复
我仔细想了想,因为ThisType也是动态生成的吧 有2中选择 1、把EmittedGetter参数写成object(改成方法而不是属性) 2、写一个抽象类,带上抽象的EmittedGetter,动态类去继承它,然后delegete使用抽象类生成
Poopaye 2017-03-14
  • 打赏
  • 举报
回复
引用 2 楼 pig357 的回复:
你好,我把委托类型改为string
楼主这智力不太适合编程啊 你只是把返回类型改成了string,但是参数类型还是object,我简写为string(object) 而你创建的方法签名是:returnType: typeof(string), parameterTypes: new[] { ThisType },简写为string(ThisType) 虽然我不知道ThisType具体是哪个类型,但看名字肯定不是object
pig357 2017-03-14
  • 打赏
  • 举报
回复
引用 1 楼 shingoscar 的回复:
一个是 string(Type),一个是object(object),楼主还不知道原因?
你好,我把委托类型改为string,也是报一样的错误。提示“”“无法绑定到目标方法,因其签名或安全透明度与委托类型的签名或安全透明度不兼容。”
 public delegate void SetPropertyValue(object obj, string value);
        public delegate string GetPropertyValue(object obj);
Poopaye 2017-03-14
  • 打赏
  • 举报
回复
就是这个意思,不过你这样设置属性
引用 11 楼 pig357 的回复:
public void Set(string propertyName,object value){ if(propertyName=="Name ") Name = value; else if(propertyName=="Age") Age= value; }
还不如用Dictionary、反射、或者dynamic呢 完全没体现出有用动态类的必要
pig357 2017-03-14
  • 打赏
  • 举报
回复
引用 10 楼 shingoscar 的回复:
不是说了可以用抽象类么
引用
你好,因为该数据结构是其它地方配置的,其格式根据环境而变化,所以大部分时间不能用抽象类表示。
你说的这些我不知道同我的方案有什么冲突的地方,又不用你定字段,只要个方法。
你好,你的意思是生成如下类似的结构么?

interface IA{
  void Set(string propertyName,object value);
}

class A:IA
{
    public string Name{get;set;}
    public int Age{get;set;}
    public void Set(string propertyName,object value){
        if(propertyName=="Name ")   Name = value;
        else if(propertyName=="Age")   Age= value;
   }
}

var a = CreateInstance() as IA;
a.Set("Name", "张三");
a.Set("Age",50);
Poopaye 2017-03-14
  • 打赏
  • 举报
回复
不是说了可以用抽象类么
引用
你好,因为该数据结构是其它地方配置的,其格式根据环境而变化,所以大部分时间不能用抽象类表示。
你说的这些我不知道同我的方案有什么冲突的地方,又不用你定字段,只要个方法。
pig357 2017-03-14
  • 打赏
  • 举报
回复
引用 8 楼 shingoscar 的回复:
那肯定啊 delegate、2次类型转换、dictionary查找 每个都有开销
请问你有没有什么其他可以优化的方法?
Poopaye 2017-03-14
  • 打赏
  • 举报
回复
那肯定啊 delegate、2次类型转换、dictionary查找 每个都有开销
pig357 2017-03-14
  • 打赏
  • 举报
回复
引用 6 楼 shingoscar 的回复:
[quote=引用 5 楼 pig357 的回复:] 请问你有什么好的办法么?
方法在4楼 顺便说下,如果有抽象类,那也用不着什么delegate,直接调用好了 实现接口也可以[/quote] 你好,因为该数据结构是其它地方配置的,其格式根据环境而变化,所以大部分时间不能用抽象类表示。 我使用你的第一种方法,现在已经能够成功运行了,但是发现效率和使用Dynamic接近,比直接定义类慢了7倍左右,不知道是不是我使用方法的原因,你帮我看看哇,O(∩_∩)O谢谢!

//测试
private void Test2()
        {
            var watch = Stopwatch.StartNew();
            string s = null;
            var st2 = new Student() {Name = "aaaaaa"};
            for (var i = 0; i < 5000000; i++)
                st2.Name = "sdasdas11";

            Console.WriteLine("native:{0}", watch.ElapsedTicks);

            var watch3 = Stopwatch.StartNew();
             s = null;
            dynamic st1 = new Student() { Name = "sdasdas" };
            for (var i = 0; i < 5000000; i++)
                st1.Name = "sdasdas22";

            Console.WriteLine("dynamic:{0}", watch3.ElapsedTicks);

            EmitHelper emit = new EmitHelper();
            emit.CreateType("DynamicAssembly", "Student2");
            emit.CreateProperty("Name", typeof(string));
            //emit.Save();
            object ee = emit.CreateInstance();
            emit.Set(ee, "Name", "sdasdas");
            var watch2 = Stopwatch.StartNew();
            for (var i = 0; i < 5000000; i++)
                emit.Set(ee, "Name", "sdasdas22");
            Console.WriteLine("emitType:{0}", watch2.ElapsedTicks); 
        }

public class EmitHelper
    {
        private TypeBuilder _typeBuilder = null;
        private AssemblyBuilder _assemblyBuilder = null;
        public Type ThisType { get; set; }

        public delegate void SetPropertyValue(object obj, object value);
        public delegate object GetPropertyValue(object obj);
        private Dictionary<string, SetPropertyValue> _dicSetProperty = new Dictionary<string, SetPropertyValue>();
        private Dictionary<string, GetPropertyValue> _dicGetProperty = new Dictionary<string, GetPropertyValue>();

        public void CreateType(string assembly, string type)
        {
            AssemblyName DemoName = new AssemblyName(assembly);
            _assemblyBuilder = AppDomain.CurrentDomain.DefineDynamicAssembly(DemoName, AssemblyBuilderAccess.RunAndSave);
            ModuleBuilder mb = _assemblyBuilder.DefineDynamicModule(DemoName.Name, DemoName.Name + ".dll");
            _typeBuilder =  mb.DefineType(type, TypeAttributes.Public);
        }

        public void CreateProperty(string propertyName, Type propertyType)
        {
            TypeBuilder tb = _typeBuilder;
            var field = tb.DefineField("_" + propertyName, propertyType, FieldAttributes.Private);
            var getMethod = tb.DefineMethod("get_" + propertyName, MethodAttributes.Public, propertyType, null);
            var setMethod = tb.DefineMethod("set_" + propertyName, MethodAttributes.Public, null, new Type[] { propertyType });
            var ilGet = getMethod.GetILGenerator();
            ilGet.Emit(OpCodes.Ldarg_0);
            ilGet.Emit(OpCodes.Ldfld, field);
            ilGet.Emit(OpCodes.Ret);

            var ilSet = setMethod.GetILGenerator();
            ilSet.Emit(OpCodes.Ldarg_0);
            ilSet.Emit(OpCodes.Ldarg_1);
            ilSet.Emit(OpCodes.Stfld, field);
            ilSet.Emit(OpCodes.Ret);

            var property = tb.DefineProperty(propertyName, PropertyAttributes.None, propertyType, null);
            property.SetGetMethod(getMethod);
            property.SetSetMethod(setMethod);

            var getMethod2 = tb.DefineMethod("get2_" + propertyName, MethodAttributes.Public, typeof(object), null);
            var ilGet2 = getMethod2.GetILGenerator();
            ilGet2.Emit(OpCodes.Ldarg_0);
            ilGet2.Emit(OpCodes.Ldfld, field);
            ilGet2.Emit(OpCodes.Ret);

            var setMethod2 = tb.DefineMethod("set2_" + propertyName, MethodAttributes.Public, null, new Type[] { typeof(object) });
            var ilSet2 = setMethod2.GetILGenerator();
            ilSet2.Emit(OpCodes.Ldarg_0);
            ilSet2.Emit(OpCodes.Ldarg_1);
            if (propertyType.IsValueType)
            {
                ilSet2.Emit(OpCodes.Unbox_Any, propertyType);// 如果是值类型,拆箱 string = (string)object;
            }
            else
            {
                ilSet2.Emit(OpCodes.Castclass, propertyType);// 如果是引用类型,转换 Class = object as Class
            }
            ilSet2.Emit(OpCodes.Stfld, field);
            ilSet2.Emit(OpCodes.Ret);

            Save();

            var dm = new DynamicMethod(name: "EmittedGetter", returnType: typeof(object), parameterTypes: new[] { typeof(object) }, owner: ThisType);
            var type = ThisType;
            var prop = type.GetMethod("get2_" + propertyName);
            var il2 = dm.GetILGenerator();
            il2.Emit(OpCodes.Ldarg_0);
            il2.Emit(OpCodes.Call, prop);
            il2.Emit(OpCodes.Ret);
            _dicGetProperty[propertyName] = dm.CreateDelegate(typeof(GetPropertyValue)) as GetPropertyValue;

            var callMethod = ThisType.GetMethod("set2_" + propertyName, BindingFlags.Instance | BindingFlags.IgnoreCase | BindingFlags.Public | BindingFlags.NonPublic);
            var para = callMethod.GetParameters()[0];
            // 创建动态函数
            DynamicMethod method = new DynamicMethod("EmittedSetter", null, new Type[] { typeof(object), typeof(object) }, ThisType);
            // 获取动态函数的 IL 生成器
            var il = method.GetILGenerator();
            il.Emit(OpCodes.Ldarg_0);
            il.Emit(OpCodes.Ldarg_1);  
            il.EmitCall(OpCodes.Callvirt, callMethod, null);//调用函数
            il.Emit(OpCodes.Ret);   // 返回
            _dicSetProperty[propertyName] = method.CreateDelegate(typeof(SetPropertyValue)) as SetPropertyValue;
        }

        public Type Save()
        {
            ThisType = _typeBuilder.CreateType();
            return ThisType;
        }

        public object CreateInstance()
        {
            return Activator.CreateInstance(ThisType);
        }

        public void Set(object obj, string propertyName, object value)
        {
            _dicSetProperty[propertyName](obj, value);
        }

        public object Get(object obj, string propertyName)
        {
            return _dicGetProperty[propertyName](obj);
        }
    }
测试结果为: native: 94071 dynamic: 705040 emitType: 677257
Poopaye 2017-03-13
  • 打赏
  • 举报
回复
一个是 string(Type),一个是object(object),楼主还不知道原因?

110,534

社区成员

发帖
与我相关
我的任务
社区描述
.NET技术 C#
社区管理员
  • C#
  • Web++
  • by_封爱
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告

让您成为最强悍的C#开发者

试试用AI创作助手写篇文章吧