问题描述
publicclassMyTestClass{privatestring_name;publicstringName{get{returnstring.Format("名字:{0}",_name);}set{value=value??string.Empty;_name=value.Substring(0,4);}}}publicstaticvoidMain(){DynamicMethodmethod=newDynamicMethod("Set_Name",typeof(string),newType[]{typeof(object),typeof(object)});varil=method.GetILGenerator();il.Emit(OpCodes.Ldarg,0);il.Emit(OpCodes.Ldarg,1);//dosomethingvarname=typeof(MyTestClass).GetProperty("Name");name.SetMethod=method;//setmethod只读}
我想在运行时修改MyTestClass类中Name属性的set方法。请问如何去实现?
解决方案
解决方案二:
将get/set方法定义成虚方法,运行时用emit创建它的派生类,重写这些方法,这是最简单的。
解决方案三:
这不是一个取值一个赋值,我没看懂你的问题
解决方案四:
怎么个改法?说具体点
解决方案五:
就像版主说的,将set和get做成虚方法,派生类重写虚方法,然后用反射发出来动态创建派生对象。
解决方案六:
一个常见的、浪漫的、但又无奈的误会:Emit可以修改编译时已存在的类
解决方案七:
引用1楼caozhy的回复:
将get/set方法定义成虚方法,运行时用emit创建它的派生类,重写这些方法,这是最简单的。
具体应该怎么弄?给点伪代码。。你这么一说我想起EF的属性的lazyload。。。
解决方案八:
引用1楼caozhy的回复:
将get/set方法定义成虚方法,运行时用emit创建它的派生类,重写这些方法,这是最简单的。
我看明白了。。你意思是想将MyTestClass的name设为virtual。我使用的时候还要,动态创建一个类(DefineType)继承MyTestClass。然后重写name属性。。然后再将属性的setvalue方法绑定为我想要的结果。。。这样成本太高了。。有没有其它办法
解决方案九:
引用5楼phommy的回复:
一个常见的、浪漫的、但又无奈的误会:Emit可以修改编译时已存在的类
我觉得应该,也许,可能行。。。
解决方案十:
我就想知道为什么要这么做……
解决方案十一:
引用9楼starfd的回复:
我就想知道为什么要这么做……
拿EF举例我实体(A)中有个属性(B)关联了其它表。我不想在查A的时候把B带出来。但我又想在A访问B属性的时候去执行查询。。现在出现了2种情况。。一种是默认全部带出来。。这时属性B直接getreturnvalue;set_field=value就可以了。。另一种情况就是我上面说的了,我就需要修改属性B的get方法。。让它执行我定义的方法,这个方法是执行查询,returnB类型大概情况就像这样,我的需求也是有2种情况产生。。因为EF可以,我就觉得肯定可以这样做。。
解决方案十二:
引用9楼starfd的回复:
我就想知道为什么要这么做……
因为EF在lazyload的时候将属性也设为了virtual。。根据版主给出的回复,我怀疑EF也是这样做的。运行时用emit创建它的派生类,重写这些属性。。
解决方案十三:
这个估计就是用了Lazy<T>而已吧……
解决方案十四:
引用10楼lyj224170707的回复:
Quote: 引用9楼starfd的回复:
我就想知道为什么要这么做……拿EF举例我实体(A)中有个属性(B)关联了其它表。我不想在查A的时候把B带出来。但我又想在A访问B属性的时候去执行查询。。现在出现了2种情况。。一种是默认全部带出来。。这时属性B直接getreturnvalue;set_field=value就可以了。。另一种情况就是我上面说的了,我就需要修改属性B的get方法。。让它执行我定义的方法,这个方法是执行查询,returnB类型大概情况就像这样,我的需求也是有2种情况产生。。因为EF可以,我就觉得肯定可以这样做。。
.我看的有点晕,我不晓得是不是理解错了,我的理解是你想让你的B属性只给A自己访问,不给外面访问。。。就这样:privatestring_name;publicstringName{privateget{returnstring.Format("名字:{0}",_name);}set{_name=value;}}
解决方案十五:
引用1楼caozhy的回复:
将get/set方法定义成虚方法,运行时用emit创建它的派生类,重写这些方法,这是最简单的。
publicclassMyTestClass{privatestring_name;publicvirtualstringName{get{returnstring.Format("名字:{0}",_name);}set{value=value??string.Empty;_name=value.Substring(0,4);}}}publicstaticvoidMain(){varassembly=AppDomain.CurrentDomain.DefineDynamicAssembly(newAssemblyName("myAss"),AssemblyBuilderAccess.RunAndSave);varmodule=assembly.DefineDynamicModule("myModule","myModule.dll");varmyClass=module.DefineType("MyTestClass2",TypeAttributes.Public,typeof(MyTestClass));varproperty_Name=myClass.DefineProperty("Name",PropertyAttributes.None,typeof(string),null);vartype=myClass.CreateType();//报错。。MyTestClass访问被拒绝objectobj=type.GetConstructor(newType[0]).Invoke(null);}
报错了--。。是不是没在同一程序集。。如何改。。
解决方案:
引用13楼lc316546079的回复:
Quote: 引用10楼lyj224170707的回复:
Quote: 引用9楼starfd的回复:
我就想知道为什么要这么做……拿EF举例我实体(A)中有个属性(B)关联了其它表。我不想在查A的时候把B带出来。但我又想在A访问B属性的时候去执行查询。。现在出现了2种情况。。一种是默认全部带出来。。这时属性B直接getreturnvalue;set_field=value就可以了。。另一种情况就是我上面说的了,我就需要修改属性B的get方法。。让它执行我定义的方法,这个方法是执行查询,returnB类型大概情况就像这样,我的需求也是有2种情况产生。。因为EF可以,我就觉得肯定可以这样做。。
.我看的有点晕,我不晓得是不是理解错了,我的理解是你想让你的B属性只给A自己访问,不给外面访问。。。就这样:privatestring_name;publicstringName{privateget{returnstring.Format("名字:{0}",_name);}set{_name=value;}}
不是。。我是想让我的Name的get执行我特定的代码。。而些特定的代码是在程序运行时动态添加的
解决方案:
你代码那个错是因为把MyTestClass类定义在了私有的Program里面,所以别的程序集的类是没法“看到”它的,当然没法继承。这种技术叫动态代理,EF的延迟加载就是用的这种技术。一般是两种方式,一种是拦截虚方法,还有就是通过接口拦截。这些功能不少动态AOP框架和IoC/DI框架都有,自己写还不如用第三方框架,比如,LightInject是个轻量级IoC容器,有扩展实现动态代理。最简单的例子:varc=newServiceContainer();c.Register<MyTestClass>();c.Intercept(mi=>mi.IsPropertyGetter(),ii=>"Hello");varm=c.GetInstance<MyTestClass>();Console.WriteLine(m.Name);
解决方案:
引用16楼github_22161131的回复:
你代码那个错是因为把MyTestClass类定义在了私有的Program里面,所以别的程序集的类是没法“看到”它的,当然没法继承。这种技术叫动态代理,EF的延迟加载就是用的这种技术。一般是两种方式,一种是拦截虚方法,还有就是通过接口拦截。这些功能不少动态AOP框架和IoC/DI框架都有,自己写还不如用第三方框架,比如,LightInject是个轻量级IoC容器,有扩展实现动态代理。最简单的例子:varc=newServiceContainer();c.Register<MyTestClass>();c.Intercept(mi=>mi.IsPropertyGetter(),ii=>"Hello");varm=c.GetInstance<MyTestClass>();Console.WriteLine(m.Name);
非常感谢你的解答,你给的就是我想要的东西。。但有个问题还想请教下usingSystem;usingSystem.Threading;usingSystem.Reflection;usingSystem.Reflection.Emit;usingSystem.Linq;usingEmit;usingSystem.Diagnostics;usingSystem.Collections.Generic;usingSystem.Collections;publicclassMyTestClass{privatestring_name;publicvirtualstringName{get{returnstring.Format("名字:{0}",_name);}set{value=value??string.Empty;_name=value.Substring(0,4);}}}classDynamicJumpTableDemo{publicstaticvoidMain(){varassembly=AppDomain.CurrentDomain.DefineDynamicAssembly(newAssemblyName("myAss"),AssemblyBuilderAccess.RunAndSave);varmodule=assembly.DefineDynamicModule("myModule","myModule.dll");varmyClass=module.DefineType("MyTestClass2",TypeAttributes.Public,typeof(MyTestClass));varmethod=myClass.DefineMethod("setvalue",MethodAttributes.Public|MethodAttributes.Virtual,null,newType[]{typeof(string)});varilm=method.GetILGenerator();varfield=myClass.DefineField("_name",typeof(string),FieldAttributes.Private);//这个不能用//varfield=myClass.BaseType.GetField("_name",BindingFlags.NonPublic|BindingFlags.Instance);ilm.Emit(OpCodes.Ldarg,0);ilm.Emit(OpCodes.Ldarg,1);ilm.Emit(OpCodes.Stfld,field);ilm.Emit(OpCodes.Ret);myClass.DefineMethodOverride(method,typeof(MyTestClass).GetProperty("Name").GetSetMethod());varobj=myClass.CreateType().GetConstructor(newType[0]).Invoke(null);varname=obj.GetType().GetProperty("Name");name.SetValue(obj,"888888888888888");}}
我在子类重写了父类中的name的setvalue方法。。问题出现在varfield=myClass.DefineField("_name",typeof(string),FieldAttributes.Private);//这个不能用//varfield=myClass.BaseType.GetField("_name",BindingFlags.NonPublic|BindingFlags.Instance);ilm.Emit(OpCodes.Ldarg,0);ilm.Emit(OpCodes.Ldarg,1);ilm.Emit(OpCodes.Stfld,field);ilm.Emit(OpCodes.Ret);我不能给MyTestClass的_name字段赋值。。这是为什么?不能对父类的属性进行操作吗?只能自己另外建1个吗?
解决方案:
原来是访问级别的问题。。蛋疼