巧用 __makeref 为结构体的私有字段赋值

巧用 __makeref 为结构体的私有字段赋值
-
-
在一些特殊的应用中,我们需要对类或结构体对象实例的私有字段赋值,比如我们需要改变某个只读属性(Readonly Property)的值,本文将重点介绍如何来实现这样的功能。特别是对结构体私有字段赋值,我们需要用到一个未公开关键字 __makeref 才能够实现。

首先我们来看看类的情况

 class Student       

{           

            private string _Name;                

            public string Name           

           {                get               

                            {                    return _Name;                }           

           }            

          private int _Age;            

          public int Age            {                get                {                    return _Age;                }            }            

          public Student()           

                    {       _Name = null;    _Age = 0;            }            

          public Student(string name, int age)           

                   {

                         _Name = name;                _Age = age;            }            

         public override string ToString()           

                  {     return string.Format("Name:{0} Age:{1}", Name, Age);            }       

 } 

上面这个Student 类有两个只读属性 Name 和 Age, 这两个只读属性只能在类的构造函数中赋值,如果我们希望在程序中动态改变这两个只读属性,正常的途径无法实现,我们需要通过反射的方法来做。下面看代码:

 

 

 //创建一个 student 实例           

Student student = new Student();            

//获取 _Name 这个私有字段的信息,注意这里必须要用           

//BindingFlags.NonPublic | BindingFlags.Instance 才可以获取到           

FieldInfo fiName = typeof(Student).GetField("_Name",                

BindingFlags.NonPublic | BindingFlags.Instance);            

 //获取 _Age 这个私有字段的信息,注意这里必须要用           

FieldInfo fiAge = typeof(Student).GetField("_Age",               

BindingFlags.NonPublic | BindingFlags.Instance);            

//为_Name 字段赋值           

 fiName.SetValue(student, "ABC");            

//为 _Age 字段赋值           

fiAge.SetValue(student, 28);            

//打印           

Console.WriteLine(student);上面代码通过反射的方式,动态修改了Name 和 Age 这两个只读属性的值

打印结果是:

Name:ABC Age:28

 

下面再看结构体的情况

struct Student       

{           

private string _Name;            

public string Name           

{               

get               

{                   

return _Name;               

}           

}            

private int _Age;            

public int Age           

{               

get               

{                   

return _Age;               

}           

}            

public Student(string name, int age)           

{               

_Name = name;               

_Age = age;           

}

 public override string ToString()           

{               

return string.Format("Name:{0} Age:{1}", Name, Age);           

}       

}

将 class 改成 struct,删除默认构造函数。

然后我们再执行前面那段反射代码,打印结果是:

Name: Age:0

为什么值没有被改变呢?

在继续下面之前,请先思考1分钟 ……

struct 和 class 的区别在于 struct 是值类型,而 class 是引用类型,值类型在作为参数传递时将把自身的复制版本传递给函数,而这个复制版本已经不是其本身。

也就是说调用 fiName.SetValue(student, "ABC"); 这个函数时,CLR 会先将student 复制一份到堆栈中,SetValue函数实际上是操作这个复制版本,或者说是 student 的克隆人的Name属性,而不是 student 本身。克隆人被改变了,但本人没有变。这也是我们无法对结构体对象实例反射赋值的原因。

到了这里我们似乎已经无路可走,难道我们就无法对值类型对象反射赋值了吗?幸运的是.Net 为我们提供了一个未公开关键字 __makeref ,这个关键字可以将一个值类型转换为一个TypedReference 类型,这个类型描述既包含指向某位置的托管指针,也包含该位置可能存储的类型的运行时表示形式的对象(引自MSDN)。

FieldInfo 这个类还提供了一个 SetValueDirect 函数,这个函数可以设置给定对象支持的字段值(引自MSDN)。这个函数需要 .net 2.0 sp1 及以上版本支持。

修改反射代码如下

//创建一个 student 实例           

 Student student = new Student();            

//获取 _Name 这个私有字段的信息,注意这里必须要用           

//BindingFlags.NonPublic | BindingFlags.Instance 才可以获取到           

FieldInfo fiName = typeof(Student).GetField("_Name",               

BindingFlags.NonPublic | BindingFlags.Instance);            

//获取 _Age 这个私有字段的信息,注意这里必须要用           

FieldInfo fiAge = typeof(Student).GetField("_Age",               

BindingFlags.NonPublic | BindingFlags.Instance);            

 //为_Name 字段赋值           

fiName.SetValueDirect(__makeref(student), "ABC");            

//为 _Age 字段赋值           

 fiAge.SetValueDirect(__makeref(student), 28);            

//打印           

Console.WriteLine(student);打印结果:

Name:ABC Age:28

修改成功。

文章出处:飞诺网(www.firnow.com):http://dev.firnow.com/course/3_program/cshapo/csharpjs/20100714/441244.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值