Search code examples
c#primary-constructor

Naming parameters in C# class primary constructor


With the introduction of primary constructors to C# classes, I find myself deciding what casing to adopt when naming the parameters of a primary constructor. Should it be camelCased or PascalCase?

Comparing it to the record type where I have seen that pascal case is preferred for its parameter(s).

record Item(int Id, string Name);

For a class primary constructor what is the recommended casing for naming the parameters?

Camel case:

class Car(string model, string year, string brand)

Pascal case:

class Car(string Model, string Year, string Brand)

Please include reasons for choosing one over the other.


Solution

  • In records, C# generates properties for primary constructor parameters. Therefore, PascalCase is appropriate.

    In non-record classes, primary constructor parameters are just parameters when used only in initializers or they may be translated into fields if used in method bodies. Therefore, camelCase is appropriate.


    Example:

    record R(int P);
    

    Is translated into (by omitting all the other things that a record contains):

    class R
    {
        private readonly int <P>k__BackingField;
    
        public int P
        {
            get
            {
                return <P>k__BackingField;
            }
            init
            {
                <P>k__BackingField = value;
            }
        }
    
        public R(int P)
        {
            <P>k__BackingField = P;
        }
    }
    

    class C(int x, int y)
    {
        public int X { get; } = x;
    
        public void Print()
        {
            Console.WriteLine(y);
        }
    }
    

    Is translated into:

    internal class C
    {
        private int <y>P;
    
        private readonly int <X>k__BackingField;
    
        public int X
        {
            get
            {
                return <X>k__BackingField;
            }
        }
    
        public C(int x, int y)
        {
            <y>P = y;
            <X>k__BackingField = x;
        }
    
        public void Print()
        {
            Console.WriteLine(<y>P);
        }
    }
    

    Note that names like <X>k__BackingField are invalid C# identifiers. This avoids conflicts with user declared identifiers and makes it impossible for you to access them (except with Reflection).

    I omitted some details. You can see all the details here Primary constructor demo, SharpLab.io. The C# compiler generates IL (Intermediate Language) where these identifiers are valid. What you see in the right panel of SharpLab is a back-translation to C#. My code snippets above are excerpts from it.