自动属性增强,反射赋值的相关

C#中字段、属性和构造函数赋值的标题

0. 目录

C#6
增加产能特色目录

提议难题

率先提议多少个难点:

1、如何促成团结的流入框架?

2、字段和自行属性的界别是怎么样?

3、字段和自行属性注明时的一向赋值和构造函数赋值有怎么着分别?

4、为啥只读字段和只读自动属性(独有get未有set访谈器卡塔尔都足以在构造函数中举行赋值?

5、反射能够给只读字段可能只读属性举办赋值吗?

6、自动属性和平日属性的界别?

那么些难题是自个儿在试着写本人的流入完成时遇上的难点。那个难题应该在攻读C#时的率先节课就应当学到了,我看英特网还会有人享受说她在面试时遇下面试官问为啥只读字段和只读自动属性能够在构造函数中举行赋值,他不曾答复上来,然后他写小说斟酌那个主题素材,却尚未吸取四个举世著名的答案,实在可惜。网络有关只读属性有些是写ReadOnly性子的,读到这几个文章间接跳过呢,老版本的C#现今看也没怎么援救。

1. 老版本代码

 1 internal class Person
 2 {
 3     public string Name { get; private set; }
 4     public int Age { get; private set; }
 5 
 6     public Person(string name,int age)
 7     {
 8         Name = name;
 9         Age = age;
10     }
11 }

经常状态下,C#的性质能够很好的帮助我们做到专门的学业,比如上边的代码。在为属性赋值的时候,我们得以在随机地方为其赋值。但是并不曾风流罗曼蒂克种疑似字段相通的表明且马上初步化的语法来简化暗许值的设定。C#6为大家带给了这种新的语法,疑似为字段赋值相符为属性赋值。

笔者们也理解,C#的习性实际上是叁个编写翻译器自动生成的民用字段、get_xxx和set_xxx、一条元数据整合,比如上面的代码编写翻译后:

图片 1

<Name>k__BackingField字段的IL

1 .field private string '<Name>k__BackingField'
2 .custom instance void [mscorlib]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( 01 00 00 00 ) 
3 .custom instance void [mscorlib]System.Diagnostics.DebuggerBrowsableAttribute::.ctor(valuetype [mscorlib]System.Diagnostics.DebuggerBrowsableState) = ( 01 00 00 00 00 00 00 00 )

代表三个私家字段,第2行分别代表那些自动是编写翻译器自动生成的,第3行代表该字段不出示在Debugger窗口中。

 

get_Name方法的IL:

 1 .method public hidebysig specialname instance string 
 2         get_Name() cil managed
 3 {
 4   .custom instance void [mscorlib]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( 01 00 00 00 ) 
 5   // Code size       7 (0x7)
 6   .maxstack  8
 7   IL_0000:  ldarg.0
 8   IL_0001:  ldfld      string csharp6.Person::'<Name>k__BackingField'
 9   IL_0006:  ret
10 } // end of method Person::get_Name

那也是八个自动生成的诀窍。

 

set_Name方法的IL:

 1 .method private hidebysig specialname instance void 
 2         set_Name(string 'value') cil managed
 3 {
 4   .custom instance void [mscorlib]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( 01 00 00 00 ) 
 5   // Code size       8 (0x8)
 6   .maxstack  8
 7   IL_0000:  ldarg.0
 8   IL_0001:  ldarg.1
 9   IL_0002:  stfld      string csharp6.Person::'<Name>k__BackingField'
10   IL_0007:  ret
11 } // end of method Person::set_Name

一直以来是叁个自动生成的不二等秘书籍。

 

Name属性的IL:

1 .property instance string Name()
2 {
3   .get instance string csharp6.Person::get_Name()
4   .set instance void csharp6.Person::set_Name(string)
5 } // end of property Person::Name

代表Name属性由一个get方法和set方法结合。

付出答案

2、特性比字段多了get/set访谈器;字段是在内部存款和储蓄器中宣称的三个内部存款和储蓄器空间,能够确切的积攒值;属性像字段一样选用,却足以有投机的代码段,能赋值取值,是因为访问属性正是调用属性的get/set方法对字段举办取值赋值(或然不操作字段卡塔 尔(阿拉伯语:قطر‎;在MSDN上,提出字段作为类的私有变量使用private/protected修饰,属性则反复作为共有属性使用public修饰;字段的读取和操作都是一向操作内部存款和储蓄器,属性是调用get/set访谈器,所以字段比属性快。

3、确切来讲,未有分别。差异仅仅是一直赋值先实践,构造函数赋值后实施。在转移的IL中间语言(C#代码先编译成IL代码,然后才编译成汇编语言卡塔尔国中,字段直接赋值和构造函数赋值是在同一个代码段中(构造函数中卡塔尔的。

4、其一难题得以和方面的主题材料朝气蓬勃并起来回答。构造函数作为实例化二个类的输入,是首先访问的。字段的第一手赋值其实也是位于构造函数中施行的,所以才说一向赋值和构造函数赋值未有分别。“只读”的范围只是由C#编译器(CL福特Explorer卡塔尔维护的,笔者觉着全名应该称为“除构造函数外只读”特别规范,那是C#语法的平整记住就能够(那是自然,直接赋值其实是坐落构造函数中举行赋值的,假诺构造函数不能赋值那只读字段未有值和尚未声Bellamy(Bellamy卡塔尔样卡塔尔;

5、其一难题又有什么不可和方面包车型客车主题素材关系起来协同回答。通过反射可以给自读字段赋值可是敬敏不谢给只读属性实行赋值(不信任的能够试一下卡塔 尔(英语:State of Qatar)。对只读字段的赋值是因为绕过了C#编写翻译器(CL福睿斯卡塔 尔(阿拉伯语:قطر‎的只读突显,对只读属性赋值的话是要么调用set访问器对字段实行赋值,因为还没set访谈器所以同意后会报错。那么难题来了,那干什么只读自动属性未有set访问器还足以在构造函数中赋值呢?其实只读自动属性在构造函数中开展赋值,实质上是对字段举办赋值,和特性的get/set访谈器未有关系。

6、差距是如何?上面从来强调自动属性,是因为电动属性和普通属性不相近,比方只读普通属性(未有set访问器卡塔 尔(阿拉伯语:قطر‎不能在构造函数中赋值。在向来不自行属性在此之前,普通属性使用手续是第一声雅培(Abbott卡塔 尔(英语:State of Qatar)个字段如_id,然后声圣元(Synutra卡塔 尔(阿拉伯语:قطر‎天性质Id,在get和set访问器中做一些操作,那几个操作大大多是对字段_id的操作,可是不时和字段未有关联。普通属性可以像字段相似通过“.”的章程调用,但又像方法同样享有代码段(普通属性凉素不开发内部存款和储蓄器空间卡塔尔国。

但是C#3.0从此未来引进了自行属性,证明方式如public
int id { get; set; },C#6.0从今以后又有了public string FirstName { get;
set; } = “Jane”。自动属性料定开发了内部存款和储蓄器空间然后才有了活动属性的直接赋值。其实在类中声称自动属性会在编译成IL中间语言中宣示八个蒙蔽字段,然后生成遮掩字段的get/set方法,然后生成get/set访谈器。这里能够分解为何只读普通属性不可能在构造函数中赋值(和直接赋值卡塔 尔(英语:State of Qatar)而只读自动属性能够在构造函数中赋值(和直接赋值卡塔 尔(阿拉伯语:قطر‎,因为随意直接赋值照旧在构造函数中赋值,生成的IL代码中的构造函数中,操作的都以隐蔽字段,并未访问属性的set访谈器。(注意这里只是说的类中的自动属性,接口中也是能够有机关属性的,可是接口的自动属性并不会变动隐敝字段只是概念get/set访问器卡塔尔国

2. 自行属性加强语法

 1 internal class Person
 2 {
 3     //声明读写属性、且初始化默认值
 4     public string Name { get; set; } = "blackheart";
 5 
 6     //声明只读属性、且初始化默认值
 7     public int Age { get; } = 1;
 8 
 9     //声明只读属性
10     public string Note { get; }
11 
12     public Person(string note)
13     {
14         //在构造器中为只读属性初始化默认值
15         Note = note;
16     }
17 
18     private void func1()
19     {
20         //error,只能在构造器中初始化
21         //Note = "123";
22         //Age = 1;
23         //可以修改,因为有set访问器
24         Name = "new name";
25     }
26 }

这种新语法会在还未有set访谈器的时候把潜伏的个人字段设置为只读字段(readonly
),只同意在评释的时候设置发轫值大概在构造器里面赋值。看看IL:

图片 2

独有Name属性具有set_Name方法,而Age和Note属性则尚未set访谈器,且相应的个体字段被安装为”initonly”,表示那是多个只读字段。

构造器方法,Name{get;set;}=”blackheart”和Age{get;}=1的发轫化操作部分被撤换来实例构造函数”.ctor”方法中。

 1 .method public hidebysig specialname rtspecialname 
 2         instance void  .ctor(string note) cil managed
 3 {
 4   // Code size       34 (0x22)
 5   .maxstack  8
 6   IL_0000:  ldarg.0
 7   IL_0001:  ldstr      "blackheart"
 8   IL_0006:  stfld      string csharp6.Person::'<Name>k__BackingField'
 9   IL_000b:  ldarg.0
10   IL_000c:  ldc.i4.1
11   IL_000d:  stfld      int32 csharp6.Person::'<Age>k__BackingField'
12   IL_0012:  ldarg.0
13   IL_0013:  call       instance void [mscorlib]System.Object::.ctor()
14   IL_0018:  nop
15   IL_0019:  nop
16   IL_001a:  ldarg.0
17   IL_001b:  ldarg.1
18   IL_001c:  stfld      string csharp6.Person::'<Note>k__BackingField'
19   IL_0021:  ret
20 } // end of method Person::.ctor

和早先的语法生成的代码能够说是同大器晚成的,均是生成为三个字段、get_xxx和set_xxx方法和相应的属性元数据,本质依然是编写翻译器的语法简化。

始于解释

通过C#变迁的IL中间语言代码能够领悟的更通晓

    public class User
    {
        public int id = 0;
        public int age { get; set; } = 1;
        public User()
        {
            id = 2;
            age = 3;
        }
    }

图片 3图片 4

能够看看,自动属性会生成一个称号为 ‘<age>k__BackingField’
的隐藏私有字段+私有字段的get/set方法+属性代码段;

能够看来IL代码生成了User的构造函数
.ctor,ctor是构造函数(Constructor卡塔 尔(英语:State of Qatar)。

无论是直接赋值照旧构造函数赋值,都以在.ctor中试行的,何况操作的都以字段,自动属性的赋值操作的是隐蔽字段。

  public interface IUser
  {
    int id { get; set; }
  }

图片 5

能够见见,接口中的自动属性并从未变动隐蔽字段。

3. 参考

C# Auto-property
enhancements

其余验证

1、上文中提到“反射能够给只读字段进行赋值不过力不胜任给只读属性举办赋值”。不能够给只读属性实行赋值是因为从没set访谈器。不过大家曾经通晓了可以给字段赋值,而且只读属性会生成隐蔽字段,那大家是还是不是足以经过给掩没字段进展赋值直接达到给机关属性赋值的目标呢?答案是足以的!

定义User的只读自动属性

    public class User
    {
        public int age { get;  } = 1;
        public User()
        {
            age = 3;
        }
    }

调节台的反光赋值代码:

            var user = new User();
            try { typeof(User).GetProperty("age").SetValue(user, 9); }
            catch{    Console.WriteLine("只读属性赋值失败");}
            typeof(User).GetField("<age>k__BackingField", BindingFlags.Instance | BindingFlags.NonPublic).SetValue(user,9);
            Console.WriteLine(user.age);
            Console.Read();

运行

图片 6

2、因为隐敝字段是个体的,所以取到隐敝字段必要  BindingFlags.NonPublic

3、只读自动属性表明不想被访谈到那干什么还要给它赋值呢?那一个主题材料……做着玩,项目中本人认为也不曾什么样用到的时机……