【橙子老哥】c# 探究属性与字段本质
hello,大家好,叕到了橙子老哥的分享时间,希望大家一起学习,一起进步。
首先祝大家国庆快乐~
其次,欢迎加入.net意社区,第一时间了解我们的动态,地址:ccnetcore.com
今天,我们来个简单的,来点关于属性与字段有趣的骚操作,废话少说,我们直接开始
1、本质
属性是什么,字段是什么,这里不在这篇文章赘述了,我来直接探究下它的原理
上代码,不解释
我们写一个类,其中包含一个私有属性,一个公开属性
class MyClass
{private string UserName { get; set;}public string Name { get; set; }public void Log(){Console.WriteLine(UserName);}
}
想要知道它到底做了什么,IL一看究竟
[NullableContext(1)]
[Nullable(0)]
internal class MyClass
{[CompilerGenerated][DebuggerBrowsable(DebuggerBrowsableState.Never)]private string <UserName>k__BackingField;[CompilerGenerated][DebuggerBrowsable(DebuggerBrowsableState.Never)]private string <Name>k__BackingField;private string UserName{[CompilerGenerated] get{return this.<UserName>k__BackingField;}[CompilerGenerated] set{this.<UserName>k__BackingField = value;}}public string Name{[CompilerGenerated] get{return this.<Name>k__BackingField;}[CompilerGenerated] set{this.<Name>k__BackingField = value;}}public void Log(){Console.WriteLine(this.UserName);}public MyClass(){base..ctor();}
}
可以看到无论属性是公开还是私有,都会新增一个字段
private string <UserName>k__BackingField;
private string <Name>k__BackingField;
而对应的set、get方法,就有私有及公开区分
private string UserName{get{return this.<UserName>k__BackingField;}set{this.<UserName>k__BackingField = value;}}public string Name{get{return this.<Name>k__BackingField;}set{this.<Name>k__BackingField = value;}}
这里的get、set,本质上就是一个方法的语法糖,会编译成对应的get方法和set方法,如果熟悉java的人,对这个印象肯定深刻
看到这里,其实答案也就写在脸上了,c# 中的属性,本质上是使用get、set方法,包了一层私有字段。私有字段会叫一个特殊名字<xxxx>k__BackingField
,低级别的c#语法是允许命名加特殊字符的,就是防止重名,因为你不能取这样的名称
2、进阶
既然清楚了属性和字段的本质,我们再来玩一下它的一些骚操作
我们可以给私有属性赋值吗?当然可以
对象的操作,C#没有什么是做不到的
例如上方代码中,我们定义的私有的UserName
属性,我们实操一下,把这个私有属性赋值
var my=new MyClass(){Name = "张三"};
var property =typeof(MyClass).GetProperty("UserName",BindingFlags.NonPublic | BindingFlags.Instance);
property.SetValue(my,"私有属性");
my.Log();最终输出:私有属性,赋值成功
这个操作也很简单,需要注意的是,在条件中,需要指明BindingFlags.NonPublic | BindingFlags.Instance
,否则也是查不出来的,因为默认的方法有一层过滤:
public PropertyInfo? GetProperty(string name){return this.GetProperty(name, BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public);}
另外,属性本质是给一个私有字段包一层,那我们理论上不是可以直接操作字段?当然可以
针对我们上面研究的本质,属性生成的字段叫做:<xxx>k__BackingField
,那我们就去找这个字段,并给它赋值
var my=new MyClass(){Name = "张三"};
var property =typeof(MyClass).GetProperty("UserName",BindingFlags.NonPublic | BindingFlags.Instance);var field =typeof(MyClass).GetField("<UserName>k__BackingField",BindingFlags.NonPublic | BindingFlags.Instance);property.SetValue(my,"私有属性");
my.Log();
field.SetValue(my,"私有字段");
my.Log();输出:私有属性 私有字段 属性和字段都修改成功
Nice,是不是很有意思?属性和字段还不直接拿捏