我应该在 C# 12 中为主构造函数参数创建私有属性吗?

Pra*_*r J 10 c# primary-constructor .net-8.0 c#-12.0

我正在使用 C# 12。在 C# 12 中我可以使用主构造函数:

实施1:

public class Calculation(int a,int b)
{
  public int Addition() => a + b;
  public int Subtraction() => a - b;
}
Run Code Online (Sandbox Code Playgroud)

实施2:

public class Calculation(int a,int b)
{
 private int _a { get; init; } = a;
 private int _b { get; init; } = b;
 public int Addition() => _a + _b;
 public int Subtraction() => _a - _b;
}
Run Code Online (Sandbox Code Playgroud)

当我像这样调用这个方法时:

console.WriteLine(new Calculation(10,25).Addition());
Run Code Online (Sandbox Code Playgroud)

两种实现都工作正常,所以我想知道使用较长的实现 2 相对于较短的实现 1 是否有任何优势。

Gur*_*ron 12

以这种方式自己创建字段/属性是没有意义的,因为在这种情况下编译器会为您创建它们(反编译@sharplab.io)。

来自教程:探索主构造函数文档(它们使用struct但类的主构造函数的处理基本相同):

public struct Distance(double dx, double dy)
{
   public readonly double Magnitude => Math.Sqrt(dx * dx + dy * dy);
   public readonly double Direction => Math.Atan2(dy, dx);

   public void Translate(double deltaX, double deltaY)
   {
      dx += deltaX;
      dy += deltaY;
   }

   public Distance() : this(0,0) { }
}
Run Code Online (Sandbox Code Playgroud)

在前面的示例中,主要构造函数属性是在方法中访问的。因此,编译器创建隐藏字段来表示每个参数。以下代码大致显示了编译器生成的内容。实际的字段名称是有效的 CIL 标识符,但不是有效的 C# 标识符。

public struct Distance
{
   private double __unspeakable_dx;
   private double __unspeakable_dy;

   public readonly double Magnitude => Math.Sqrt(__unspeakable_dx * __unspeakable_dx + __unspeakable_dy * __unspeakable_dy);
   public readonly double Direction => Math.Atan2(__unspeakable_dy, __unspeakable_dx);

   public void Translate(double deltaX, double deltaY)
   {
       __unspeakable_dx += deltaX;
       __unspeakable_dy += deltaY;
   }

   public Distance(double dx, double dy)
   {
       __unspeakable_dx = dx;
       __unspeakable_dy = dy;
   }
   public Distance() : this(0, 0) { }
}
Run Code Online (Sandbox Code Playgroud)

潜在的缺点是生成的字段是可变的:

public class Calculation(int a, int b)
{
    public void Mutate() => a = 100;
    // ...
}
Run Code Online (Sandbox Code Playgroud)

尽管您的方法并不能解决这个问题,但当您混淆_a和时,会带来一些混乱a。目前无法标记生成的字段readonly(但有该功能的计划 - 请参阅此 LDM 注释文档此提案),但您可以通过使用具有相同名称(或属性)的字段来隐藏 ctor 参数:

public class Calculation2(int a, int b)
{
    private readonly int a = a;    
    private readonly int b = b;
    // public void Mutate() => a = 100; // does not compile
    public int Addition() => a + b;
    public int Subtraction() => a - b;
}
Run Code Online (Sandbox Code Playgroud)

也可以看看:

  • 我真的很讨厌修复可变问题。(不是对你的答案的批评,而是对微软当前实施的批评。)除非你知道它正在修复什么,否则`private readonly int a = a; ` 有点WTF。 (5认同)
  • @MatthewWatson:他们[计划在 C# 13 的早期预览版中修复此问题](https://github.com/dotnet/csharplang/blob/main/meetings/2023/LDM-2023-07-31.md)。 (3认同)

Mik*_*kis 8

第二种实现的优点:

  1. 它由更多行代码组成。(如果您通过编写的代码行获得报酬,则很有用。)
  2. 阅读起来比较困难。(更长的直接结果。)
  3. 它引入了一些完全不必要的私有属性,这些属性替换了编译器本来创建的用于存储构造函数参数的字段,从而使其在调试运行时运行得稍微慢一些,而这往往是未优化的。
  4. 它打开了这样的可能性:在代码的不同位置,您将无意中访问构造函数参数和私有属性,从而导致该参数占用双倍存储空间。
  5. 它给 python 程序员带来了一种温暖模糊的感觉,通过硬塞进一种原本很好的编码风格“带下划线的前缀私有”约定,这在 python 中可能是必要的,但在 C# 中是完全没有根据的。
  6. 它在很大程度上违背了拥有默认构造函数的初衷。

  • @Dai 下划线使代码看起来不必要的技术性。自从人类超越C语言阶段以来,它们就应该被抛弃。使用“this.”作为实例字段是另一种完全没有根据的变态。你不需要“这个”。为了区分静态字段和实例字段之间的区别,自本世纪初以来,我们采用了***语法突出显示***。如果您发现语法突出显示没有为您提供足够的视觉线索,请考虑切换到深色模式,这会放大其效果。 (2认同)