如何在程序运行时动态修改一个类的属性的get;set;?

游离失所 2015-08-19 03:48:39


public class MyTestClass
{
private string _name;
public string Name
{
get
{
return string.Format("名字:{0}", _name);
}
set
{
value = value ?? string.Empty;
_name = value.Substring(0, 4);
}
}
}
public static void Main()
{
DynamicMethod method = new DynamicMethod("Set_Name", typeof(string), new Type[] { typeof(object), typeof(object) });
var il = method.GetILGenerator();
il.Emit(OpCodes.Ldarg, 0);
il.Emit(OpCodes.Ldarg, 1);
//do something

var name = typeof(MyTestClass).GetProperty("Name");
name.SetMethod = method;//setmethod只读
}


我想在运行时修改MyTestClass类中Name属性的set方法。请问如何去实现?
...全文
378 18 打赏 收藏 转发到动态 举报
写回复
用AI写文章
18 条回复
切换为时间正序
请发表友善的回复…
发表回复
游离失所 2015-08-20
  • 打赏
  • 举报
回复
原来是访问级别的问题。。蛋疼
游离失所 2015-08-20
  • 打赏
  • 举报
回复
引用 16 楼 github_22161131 的回复:
你代码那个错是因为把MyTestClass类定义在了私有的Program里面,所以别的程序集的类是没法“看到”它的,当然没法继承。 这种技术叫动态代理,EF的延迟加载就是用的这种技术。一般是两种方式,一种是拦截虚方法,还有就是通过接口拦截。这些功能不少动态AOP框架和IoC/DI框架都有,自己写还不如用第三方框架,比如LightInject.Interception,LightInject是个轻量级IoC容器,有扩展实现动态代理。最简单的例子:

var c = new ServiceContainer();
c.Register<MyTestClass>();
c.Intercept(mi => mi.IsPropertyGetter(), ii => "Hello");

var m = c.GetInstance<MyTestClass>();
Console.WriteLine(m.Name);
非常感谢你的解答,你给的就是我想要的东西。。但有个问题还想请教下

using System;
using System.Threading;
using System.Reflection;
using System.Reflection.Emit;
using System.Linq;
using Emit;
using System.Diagnostics;
using System.Collections.Generic;
using System.Collections;
public class MyTestClass
{
    private string _name;
    public virtual string Name
    {
        get
        {
            return string.Format("名字:{0}", _name);
        }
        set
        {
            value = value ?? string.Empty;
            _name = value.Substring(0, 4);
        }
    }
}
class DynamicJumpTableDemo
{

    public static void Main()
    {
        var assembly = AppDomain.CurrentDomain.DefineDynamicAssembly(new AssemblyName("myAss"), AssemblyBuilderAccess.RunAndSave);
        var module = assembly.DefineDynamicModule("myModule", "myModule.dll");
        var myClass = module.DefineType("MyTestClass2", TypeAttributes.Public, typeof(MyTestClass));
        var method = myClass.DefineMethod("setvalue", MethodAttributes.Public | MethodAttributes.Virtual, null, new Type[] { typeof(string) });
        var ilm = method.GetILGenerator();
        var field = myClass.DefineField("_name", typeof(string), FieldAttributes.Private);
        //这个不能用
        //var field = 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());

        var obj = myClass.CreateType().GetConstructor(new Type[0]).Invoke(null);
        var name = obj.GetType().GetProperty("Name");
        name.SetValue(obj, "888888888888888");
    }
}
我在子类重写了父类中的name的setvalue方法。。问题出现在 var field = myClass.DefineField("_name", typeof(string), FieldAttributes.Private); //这个不能用 //var field = 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个吗?
winnowc 2015-08-19
  • 打赏
  • 举报
回复
你代码那个错是因为把MyTestClass类定义在了私有的Program里面,所以别的程序集的类是没法“看到”它的,当然没法继承。 这种技术叫动态代理,EF的延迟加载就是用的这种技术。一般是两种方式,一种是拦截虚方法,还有就是通过接口拦截。这些功能不少动态AOP框架和IoC/DI框架都有,自己写还不如用第三方框架,比如LightInject.Interception,LightInject是个轻量级IoC容器,有扩展实现动态代理。最简单的例子:

var c = new ServiceContainer();
c.Register<MyTestClass>();
c.Intercept(mi => mi.IsPropertyGetter(), ii => "Hello");

var m = c.GetInstance<MyTestClass>();
Console.WriteLine(m.Name);
游离失所 2015-08-19
  • 打赏
  • 举报
回复
引用 13 楼 lc316546079 的回复:
[quote=引用 10 楼 lyj224170707 的回复:] [quote=引用 9 楼 starfd 的回复:] 我就想知道为什么要这么做……
拿EF举例 我实体(A)中有个属性(B)关联了其它表。我不想在查A的时候把B带出来。但我又想在A访问B属性的时候去执行查询。。 现在出现了2种情况。。 一种是默认全部带出来。。这时属性B直接get return value;set _field=value就可以了。。 另一种情况就是我上面说的了,我就需要修改属性B的get方法。。让它执行我定义的方法,这个方法是执行查询,return B类型 大概情况就像这样,我的需求也是有2种情况产生。。因为EF可以,我就觉得肯定可以这样做。。[/quote]. 我看的有点晕,我不晓得是不是理解错了,我的理解是你想让你的B属性只给A自己访问,不给外面访问。。。就这样:

		private string _name;
		public string Name
		{
			private get
			{
				return string.Format("名字:{0}", _name);
			}
			set
			{
				_name = value;
			}
		}
[/quote] 不是。。我是想让我的Name的get执行我特定的代码。。而些特定的代码是在程序运行时动态添加的
_lee_chong 2015-08-19
  • 打赏
  • 举报
回复
引用 10 楼 lyj224170707 的回复:
[quote=引用 9 楼 starfd 的回复:] 我就想知道为什么要这么做……
拿EF举例 我实体(A)中有个属性(B)关联了其它表。我不想在查A的时候把B带出来。但我又想在A访问B属性的时候去执行查询。。 现在出现了2种情况。。 一种是默认全部带出来。。这时属性B直接get return value;set _field=value就可以了。。 另一种情况就是我上面说的了,我就需要修改属性B的get方法。。让它执行我定义的方法,这个方法是执行查询,return B类型 大概情况就像这样,我的需求也是有2种情况产生。。因为EF可以,我就觉得肯定可以这样做。。[/quote]. 我看的有点晕,我不晓得是不是理解错了,我的理解是你想让你的B属性只给A自己访问,不给外面访问。。。就这样:

		private string _name;
		public string Name
		{
			private get
			{
				return string.Format("名字:{0}", _name);
			}
			set
			{
				_name = value;
			}
		}
游离失所 2015-08-19
  • 打赏
  • 举报
回复
引用 1 楼 caozhy 的回复:
将get/set方法定义成虚方法,运行时用emit创建它的派生类,重写这些方法,这是最简单的。


    public class MyTestClass
    {
        private string _name;
        public virtual string Name
        {
            get
            {
                return string.Format("名字:{0}", _name);
            }
            set
            {
                value = value ?? string.Empty;
                _name = value.Substring(0, 4);
            }
        }
    }
    public static void Main()
    {
        var assembly = AppDomain.CurrentDomain.DefineDynamicAssembly(new AssemblyName("myAss"), AssemblyBuilderAccess.RunAndSave);
        var module = assembly.DefineDynamicModule("myModule", "myModule.dll");        
        var myClass = module.DefineType("MyTestClass2", TypeAttributes.Public, typeof(MyTestClass));
        var property_Name = myClass.DefineProperty("Name", PropertyAttributes.None, typeof(string), null);
        var type = myClass.CreateType();//报错。。MyTestClass访问被拒绝
        object obj = type.GetConstructor(new Type[0]).Invoke(null);
    }
报错了- -。。是不是没在同一程序集。。如何改。。
  • 打赏
  • 举报
回复
这个估计就是用了 Lazy<T>而已吧……
游离失所 2015-08-19
  • 打赏
  • 举报
回复
引用 9 楼 starfd 的回复:
我就想知道为什么要这么做……
因为EF在lazyload的时候将属性也设为了virtual。。根据版主给出的回复,我怀疑EF也是这样做的。 运行时用emit创建它的派生类,重写这些属性。。
游离失所 2015-08-19
  • 打赏
  • 举报
回复
引用 9 楼 starfd 的回复:
我就想知道为什么要这么做……
拿EF举例 我实体(A)中有个属性(B)关联了其它表。我不想在查A的时候把B带出来。但我又想在A访问B属性的时候去执行查询。。 现在出现了2种情况。。 一种是默认全部带出来。。这时属性B直接get return value;set _field=value就可以了。。 另一种情况就是我上面说的了,我就需要修改属性B的get方法。。让它执行我定义的方法,这个方法是执行查询,return B类型 大概情况就像这样,我的需求也是有2种情况产生。。因为EF可以,我就觉得肯定可以这样做。。
  • 打赏
  • 举报
回复
我就想知道为什么要这么做……
游离失所 2015-08-19
  • 打赏
  • 举报
回复
引用 5 楼 phommy 的回复:
一个常见的、浪漫的、但又无奈的误会: Emit可以修改编译时已存在的类
我觉得应该,也许,可能行。。。
游离失所 2015-08-19
  • 打赏
  • 举报
回复
引用 1 楼 caozhy 的回复:
将get/set方法定义成虚方法,运行时用emit创建它的派生类,重写这些方法,这是最简单的。
我看明白了。。你意思是想将MyTestClass的name设为virtual。 我使用的时候还要,动态创建一个类(DefineType)继承MyTestClass。然后重写name属性。。 然后再将属性的setvalue方法绑定为我想要的结果。。。 这样成本太高了。。有没有其它办法
游离失所 2015-08-19
  • 打赏
  • 举报
回复
引用 1 楼 caozhy 的回复:
将get/set方法定义成虚方法,运行时用emit创建它的派生类,重写这些方法,这是最简单的。
具体应该怎么弄?给点伪代码。。 你这么一说我想起EF的属性的lazyload。。。
phommy 2015-08-19
  • 打赏
  • 举报
回复
一个常见的、浪漫的、但又无奈的误会: Emit可以修改编译时已存在的类
smthgdin_020 2015-08-19
  • 打赏
  • 举报
回复
就像版主说的,将set和get做成虚方法,派生类重写虚方法,然后用反射发出来动态创建派生对象。
_lee_chong 2015-08-19
  • 打赏
  • 举报
回复
怎么个改法?说具体点
wc_ling 2015-08-19
  • 打赏
  • 举报
回复
这不是一个取值一个赋值,我没看懂你的问题
threenewbee 2015-08-19
  • 打赏
  • 举报
回复
将get/set方法定义成虚方法,运行时用emit创建它的派生类,重写这些方法,这是最简单的。

110,499

社区成员

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

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

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