Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Should I create private properties for primary constructor parameters in C# 12?

I'm using C# 12. In C# 12 I can use primary constructor:

Implementation 1 :

public class Calculation(int a,int b)
{
  public int Addition() => a + b;
  public int Subtraction() => a - b;
}

Implementation 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;
}

When I'm calling this method like :

console.WriteLine(new Calculation(10,25).Addition());

Both implementation working fine, so I'd like to know whether there is any advantage to using the longer Implementation 2 over the shorter Implementation 1.

like image 216
Prasanna Kumar J Avatar asked Sep 14 '25 16:09

Prasanna Kumar J


1 Answers

There is no point in creating fields/properties yourself this way, since the compiler will create them for you in this case (decompilation @sharplab.io).

From the Tutorial: Explore primary constructors doc (they use struct but the handling of the primary ctor for classes is basically the same):

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) { }
}

In the previous example, the primary constructor properties are accessed in a method. Therefore the compiler creates hidden fields to represent each parameter. The following code shows approximately what the compiler generates. The actual field names are valid CIL identifiers, but not valid C# identifiers.

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) { }
}

The potential downside is that the generated field is mutable:

public class Calculation(int a, int b)
{
    public void Mutate() => a = 100;
    // ...
}

Though your approach does not fix that but introduces some clutter when you can confuse _a and a. There is no way to mark the generated field as readonly for now (but there are plans for the feature - see this LDM notes doc and this proposal) but you can shadow the ctor parameter by using field with the same name (or property):

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;
}

See also:

  • Why do C# 12 primary constructors execute in opposite order?
  • Null checking with primary constructor in C# 12
like image 150
Guru Stron Avatar answered Sep 16 '25 05:09

Guru Stron