您的位置:澳门新葡8455最新网站 > 编程教学 > 自动属性加强,反射赋值的连锁

自动属性加强,反射赋值的连锁

发布时间:2019-12-13 19:07编辑:编程教学浏览(63)

    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#编写翻译器(CLKuga)维护的,作者感到全名应该称为“除构造函数外只读”尤其典型,那是C#语法的平整记住就能够(那是本来,直接赋值其实是投身布局函数中张开赋值的,假使构造函数不可能赋值那只读字段未有值和未有声雅培(Beingmate卡塔尔(英语:State of Qatar)样);

    5、其一难题又有啥不可和地点的主题材料联系起来同盟回答。通过反射能够给自读字段赋值可是敬谢不敏给只读属性举行赋值(不相信任的能够试一下)。对只读字段的赋值是因为绕过了C#编写翻译器(CL宝马7系)的只读呈现,对只读属性赋值的话是照旧调用set访谈器对字段实行赋值,因为从没set访问器所以同意后会报错。那么难题来了,那为啥只读自动属性未有set访谈器仍可以够在布局函数中赋值呢?其实只读自动属性在布局函数中张开赋值,实质上是对字段实行赋值,和质量的get/set访谈器未有提到。

    6、分别是什么样?下边平素强调自动属性,是因为机关属性和通常属性不等同,举例只读普通属性(未有set访谈器)不能够在结构函数中赋值。在还没活动属性在此之前,普通属性使用手续是率先声多美滋个字段如_id,然后声Bellamy(Bellamy卡塔尔(英语: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中间语言中声多美滋(Dumex卡塔尔(英语:State of Qatar)(Nutrilon卡塔尔(英语:State of Qatar)(Beingmate卡塔尔(قطر‎个隐敝字段,然后生成隐瞒字段的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、只读自动属性表达不想被访谈到那为啥还要给它赋值呢?这一个题目……做着玩,项目中本身感到也尚无什么样用到的机遇……

    本文由澳门新葡8455最新网站发布于编程教学,转载请注明出处:自动属性加强,反射赋值的连锁

    关键词:

上一篇:没有了

下一篇:没有了