图片 6

构造函数赋值,自动属性增强

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方法对字段实行取值赋值(大概不操作字段卡塔 尔(英语:State of Qatar);在MSDN上,提出字段作为类的村办变量使用private/protected修饰,属性则频频作为共有属性使用public修饰;字段的读取和操作都以直接操作内部存款和储蓄器,属性是调用get/set访谈器,所以字段比属性快。

3、准确来讲,未有区分。分裂仅仅是一贯赋值先举办,构造函数赋值后实施。在转移的IL中间语言(C#代码先编写翻译成IL代码,然后才编写翻译成汇编语言卡塔尔国中,字段直接赋值和构造函数赋值是在同五个代码段中(构造函数中卡塔 尔(英语:State of Qatar)的。

4、其风流倜傥题材得以和方面包车型客车主题材料一同起来回答。构造函数作为实例化多少个类的进口,是初次访谈的。字段的直白赋值其实也是献身构造函数中实行的,所以才说一贯赋值和构造函数赋值没有差异。“只读”的范围只是由C#编写翻译器(CL奇骏卡塔尔国维护的,小编感觉全名应该称为“除构造函数外只读”尤其可靠,那是C#语法的规规矩矩记住就能够(那是当然,直接赋值其实是身处构造函数中开展赋值的,假如构造函数不能够赋值那只读字段未有值和未有声美素佳儿(Aptamil卡塔 尔(阿拉伯语:قطر‎(Dumex卡塔 尔(英语:State of Qatar)样卡塔尔国;

5、以此主题材料又足以和上面包车型地铁标题关系起来合作回答。通过反射能够给自读字段赋值但是力所不及给只读属性进行赋值(不相信赖的能够试一下卡塔尔。对只读字段的赋值是因为绕过了C#编写翻译器(CL奇骏卡塔 尔(阿拉伯语:قطر‎的只读展现,对只读属性赋值的话是要么调用set访谈器对字段举行赋值,因为从没set访谈器所以同意后会报错。那么难题来了,那干什么只读自动属性未有set访谈器还能在构造函数中赋值呢?其实只读自动属性在构造函数中打开赋值,实质上是对字段实行赋值,和品质的get/set访谈器未有提到。

6、分别是何等?下边平昔重申自动属性,是因为机关属性和平时属性不等同,比方只读普通属性(未有set访谈器卡塔 尔(阿拉伯语:قطر‎不能够在构造函数中赋值。在还未机关属性在此之前,普通属性使用手续是率先声多美滋个字段如_id,然后声美赞臣(Meadjohnson卡塔 尔(英语:State of Qatar)特质量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访谈器。这里能够分解为何只读普通属性不可能在构造函数中赋值(和向来赋值卡塔尔而只读自动属性能够在构造函数中赋值(和直接赋值卡塔 尔(阿拉伯语:قطر‎,因为无论是间接赋值还是在构造函数中赋值,生成的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卡塔尔国。

不管直接赋值照旧构造函数赋值,都以在.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、只读自动属性表达不想被访问到那干什么还要给它赋值呢?这一个主题材料……做着玩,项目中自己觉着也并没有啥用到的空子……

发表评论

电子邮件地址不会被公开。 必填项已用*标注