标签:
2.【.Net底层剖析】2.stfld指令-给对象的字段赋值
未完待续......
我们经常在code中用到属性,但是我们真的知道属性和字段的区别吗?为什么会有属性这个用法?带着这两个问题,我们来用IL中间语言剖析一下属性(Property)
1
|
public string name { get ; set ; } |
编译之后,用ildasm.exe工具看下IL代码
ildasm.exe 路径:C:\Program Files (x86)\Microsoft SDKs\Windows\v10.0A\bin\NETFX 4.6.1 Tools 不同的机器,ildasm.exe路径可能不同
在我们分析IL代码之前,我们需要了解下IL的符号代表的含义:
双击进去就可以看到详细的IL代码:
双击进去查看IL代码
私有字段<Name>K__BackingField,string类型,
ldarg要特别注意一个问题:如果是实例方法的话ldarg.0加载的是本身,也就是this,ldarg.1加载的才是函数的第一个参数;如果是静态函数,ldarg.0就是第一个参数。
所以get_Name的作用就是得到隐藏字段<Name>k__BackingField的值
s.Name相当于得到了隐藏字段<Name>k__BackingField的值
所以set_Name的作用就是将set_Name中的参数value赋值给隐藏字段<Name>k__BackingField
s.Name = "Jackson" 相当于value="Jackson",将value赋值给<Name>k__BackingField
newobj指令:
所有的对象都包含这两个附加成员,用于管理对象。
所以s.Name就是调用set_Name(string value)方法,将"Jackson"的值传给set_Name,setName方法中将value的值赋值给隐藏字段<Name>k__BackingField
上面的Name属性是一个自动实现的属性
下面我们来显示实现Name的get和set访问器方法
查看get_Name和set_Name的IL代码,与自动属性的get_Name和set_Name方法的IL的代码的区别是多了一行IL_0000: nop和代码大小。
那么显示实现getter和setter访问器有什么好处呢?
1.如果开始使用自动实现的属性public string Name { get; set; },访问该属性的任何代码实际都会调用get和set方法。如果以后决定自己实现get方法和/或set方法,而不是接收编译器的默认实现,访问属性的任何代码都不必重新编译。然后,如果将Name什么为字段,以后又想它更改为属性,那么访问字段的所有代码都必须重新编译,以便访问属性的方法。
2.自动实现的属性,不能再get和set方法上添加一个断电,所以不好检测应用程序在什么时候获取或设置这个属性。相反,手动实现的属性可设置断点,查错时显得非常方便。但是调试时需要注意,如果对属性Name添加了监视,则可能会引入bug,比如在get访问器中递增一个字段count,那么每单步执行一行代码,监视器都会重新去调用get方法,从而造成字段的递增。
看下面的code,如果不进行debug,count=1,如果进行了debug,count值会随着监视Name属性而调用get方法的次数不同而不同。
解决办法是在Visual Studio中关闭属性求值,工具->选项->调试->常规->不勾选启用属性求值和其他隐式函数调用。
如果需要对监视器中的属性求值,可以手动强制属性求值:
注意:
get和set方法要么都显示实现,要么都自动实现
1.简单的get和set访问其方法,release版本中,JIT将代码内联(inline)。使用属性就没有性能上的损失。
2.JIT编译器在调试代码时不会内联属性方法,因为内联的代码回变得难以调试。
3.在程序的release版本中,访问属性时的性能可能比较快,在程序的调试版本中,则可能比较慢。
4.字段访问无论在调试还是release版本中很快
通过上面的IL代码分析,我们已经对属性这个语法糖的本质更加清楚了。那么既然属性就是两个方法构成的,那么我们可以设置get_Name和get_Name方法的访问权限吗?
答案是肯定的。如下图所示,当我们想要对象s的Name属性时,提示get访问器不能访问
那么我们是否能删掉其中一个get或set方法呢?
答案是不能。如下图所示:提示必须定义get和set访问器
注意:
1.属性的访问级别可以是指定为任意访问级别
2.get和set访问器的访问级别必须等于属性的访问级别或比属性的访问级别更大,如属性Name的访问级别是public,则get和set访问器的访问级别必须是public,或是protected,private等
3.get和set访问器中只能有一个访问级别高于属性的访问级别,另外一个必须等于属性的访问级别
4.get和set访问器的访问级别如果等于属性的访问级别,则必须省略访问性的关键字。如属性Name的访问级别是public,get和set的访问级别是public,则必须省略get和set前面的public,否则VS编译不通过。
属性其实是由两个访问器方法get_Name()和set_Name()和一个隐藏字段<Name>k__BackingField构成。
1.属性中的get_Name和set_Name方法我们可以自己实现,从而可以在方法中加一些对数据的合理性检查,确保对象的状态永远不被破坏。其他的用法如:在WPF可以利用属性实现动态绑定。
2.封装了字段的访问性。可以设置get方法是public的,set方法是private的,那么这个属性就是只读的。