考虑到读者的水平有高有低,照顾下新人,首先来解释下,什么是隐式属性,什么是显示属性
隐式属性是一种自动实现的属性,其中编译器会为属性生成相应的字段。隐式属性的语法更简单,只需要声明一个属性,而不需要显式声明一个对应的字段。例如:
public class Person { public string Name { get; set; } public int Age { get; set; } }
在上面的示例中,Name 和 Age 属性都是隐式属性。编译器会自动为这些属性生成相应的字段。
显示属性是一种需要显式声明对应字段的属性。显示属性提供了更多的控制和保护,但语法更繁琐。例如:
public class Person { private string name; private int age; public string Name { get { return name; } set { name = value; } } public int Age { get { return age; } set { age = value; } } }
在上面的示例中,Name 和 Age 属性都是显示属性。需要显式声明相应的字段,并在属性的 get 和 set 访问器中进行相应的操作。
总的来说,隐式属性比较方便,而且可以使代码更加简洁。但是,如果需要更多的控制和保护,或者需要自定义 get 和 set 访问器的行为,就需要使用显示属性。
C# 为属性提供了很多支持,允许通过属性清晰地表达出自己的设计思路,而且当前的 C# 语言还允许我们很方便地修改这些属性。如果你一开始就能采用属性来编写代码,那么以后便可以从容地应对各种变化。
在向类中添加可供访问的数据时,要实现的属性访问器通常很简单,只是对相应的数据字段做一层包装而已。在这种情况下,其实可以采用隐式写法来创建属性,从而令代码变得更加简洁:
public string Name { get; set; }
编译器会生成一个名字来表示与该属性相对应的后援字段。可以用属性的 setter 修改这个后援字段的值。由于该字段是编译器生成的,因此,即便在自己所写的类中,也得通过属性访问器进行操作,而不是直接修改字段本身。这种区别其实并不会造成太大影响,因为编译器所生成的属性访问器中只包含一条简单的赋值语句,因此,很有可能得到内联,这样一来,通过属性访问器来操纵数据就和直接操纵后援字段差不多了。从程序运行时的行为来看,访问隐式属性与访问后援字段是一样的,就算从性能角度观察,也是如此。
扩充下面再来讲一下什么是后援字段:
C#中的后援字段(backing field)指的是在属性(property)内部用于存储属性值的变量。当我们声明一个属性时,实际上是声明了一个可以读写的公共接口,用于访问后援字段中的值。
例如,我们可以声明一个名为“Age”的属性:
public int Age { get; set; }
在这个属性声明中,我们没有显式地定义后援字段,但实际上编译器会自动创建一个名为“<Age>k__BackingField”的私有字段来存储Age属性的值。我们可以使用反编译工具来查看编译后的代码:
[CompilerGenerated] private int <Age>k__BackingField; public int Age { [CompilerGenerated] get { return <Age>k__BackingField; } [CompilerGenerated] set { <Age>k__BackingField = value; } }
在上面的代码中,我们可以看到编译器自动生成的后援字段和属性的定义。由于后援字段是私有的,因此无法直接访问,但我们可以通过公共的属性访问它的值。
在一些情况下,我们可能需要自定义后援字段的名称,例如:
private int _age; public int Age { get { return _age; } set { _age = value; } }
在这个例子中,我们使用名为“_age”的后援字段来存储Age属性的值,而不是使用编译器自动生成的后援字段。这种方式可以让我们更好地控制属性值的存储和访问,但也会增加代码的复杂度。因此,在大多数情况下,使用编译器自动生成的后援字段是更好的选择。