表达式体属性和简单的getter属性之间的区别

4
在阅读不同的C#项目源代码后,我注意到了关于公共“getter”和私有“setter”属性编写几乎相同语句的不同方式。
第一种只使用属性的方法如下:
public int x { get; private set; }

第二种方法是使用表达式属性:

private int _x;
public int x => _x;

我知道public int x { get; }等同于:
private readonly int __x;
public int x { get { return __x } }

我理解单个"getter"的情况下,表达式主体和常规属性之间的区别。 但是当存在持有引用值的私有字段时,我不理解它们之间的区别。我以为第二种更快,因为您可以直接访问字段,而不必在类内调用方法。 这只是一种风格上的差别,还是其中一个例子更快、更健壮等?


表达式主体成员每次访问时都会执行表达式,而其他类型的属性初始化器只会执行一次。具有私有 setter 的属性可以更改其值,而没有 setter 的属性只能在构造函数运行时进行初始化(因此是只读的)。 - Zohar Peled
3个回答

3

你有两组等效的结构。

第一组

当你不需要在构造函数外部访问备份字段时,可以使用以下任何一种结构:

  • 最初的回答
private readonly int _x;
public int x => _x;

或者

private readonly int _x;
public int x { get => _x; }

或者

private readonly int _x;
public int x { get { return _x; } }

或者

public int x { get; }

第二组

当您需要在构造函数外部访问后备字段时,可以使用以下任何构造:

  • 使用公共属性(Public Property)
  • 使用Get/Set方法
  • 使用内部方法

这些方法都可以让您在构造函数之外访问后备字段。

private int _x;
public int x => _x;

或者

private int _x;
public int x { get => _x; }

或者

private int _x;
public int x { get { return _x; } }

或者

public int x { get; private set; }

你可以期望所有的替代方案都同样快。在最后一个构造中,编译器将会注入一个setter方法(对于每个自动属性,它也会注入一个backer field)。在其他情况下,你直接访问该字段。注入的setter几乎肯定会被JIT内联,这消除了方法调用的性能惩罚。有关JIT内联的详细信息,请参阅此Q&A
自动属性确实更为简洁,使你的代码更整洁,特别是当你有许多属性时。但归根结底,这取决于个人喜好(或团队的编码规则)。

0
如果您使用私有后备字段,那么您正在封装信息并创建更健壮的代码。在某些情况下,它还可以提高可读性。
此外,事实上该值安全地存储在字段中,因此如果您的getter和setter中有需要在将来更改的逻辑,则与实际值存储分离,使得未来更改该实现更容易。

最后一部分很有趣。那实际上是一个很好的观点,我没有想到你的API可以更加一致。 - number2

0
请注意,如果您在构造函数中初始化属性,则甚至还有另一种可能性。
public int X { get; }

这是在 C# 6.0 中引入的只读属性。你可以在构造函数中赋值(然后再也不修改它)。

public MyClass (int x) // Constructor
{
    X = x;
}

或者使用初始化程序

public int X { get; } = 100;

对于这些事情,你不应该关心速度。创建易于阅读和稳健的代码。C#编译器或Jitter(应用程序启动时运行的即时编译器,并在首次调用方法时)可能会内联代码,甚至不调用getter或setter。

普通属性、方法和构造函数与表达式体属性、方法和构造函数之间的区别仅仅是语法上的差异。在行为上没有区别。两种变体产生相同的编译后IL代码,因此速度上没有区别。

public int X => _x;只是public int X { get { return _x; } }的简化语法。这也被称为语法糖


表达式成员将在每次访问时执行表达式。这是开发人员需要注意的微妙差别。 - Zohar Peled
有什么区别呢?return每次访问时都会执行表达式。请注意,在public int X { get; } = 100;中,= 100是一个初始化器,只会执行一次。但是getter仍然会在每次调用时执行return _hiddenBackingField(为100)(这里不涉及编译器优化)。 - Olivier Jacot-Descombes
是的,尝试使用DateTime.Now。区别很明显。 - Zohar Peled

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接