0

Are these property declarations identical?

public int MyProperty { get; private set;}
public int MyProperty { get; }

And also these ones?

public int MyProperty { get; } = 123;
public int MyProperty => 123;

I am asking because I saw mix of these in a source code and they seem to me identical. So I wonder why do they mix coding style in one piece of software, and I need to be sure if I understand it correctly or not. (I mean two separate pairs: line 1 = line 2, and line 3 = line 4.) If not identical, please explain the difference, thanks.

(Note this is not a homework or something. Although it looks like. ;-))

Al Kepp
  • 5,831
  • 2
  • 28
  • 48

2 Answers2

1
public int MyProperty { get; private set;}
public int MyProperty { get; }

are not identical

public class Myclass
{
    public int MyProperty { get; private set; }
    public int MyProperty2 { get; }

    public void MyFunc()
    {
        MyProperty = 5; //This Is Legal

        MyProperty2 = 6; //This is not
    }
}

Similarly for the next pair

public class Myclass
{
    public int MyProperty { get; } = 123;
    public int MyProperty2 => 123;

    public Myclass()
    {
        MyProperty = 65; //This is Legal
        MyProperty2 = 104; // This is not
    }
}

MyProperty2 is just a quick syntax for a method that always returns 123. There's no underlying field to it. Hence 123 gets evaluated every time you call it. MyProperty is a field which is set to 123 when the Myclass is initialized, and then the get method returns that field. Hence the field can be changed in the constructor.

Yair Halberstadt
  • 5,733
  • 28
  • 60
  • 1
    Also a big difference between the 2 of the 2nd pair, the value is evaluated once in `public int MyProperty { get; } = 123;` however the value is evaluated every time in `public int MyProperty2 => 123;`, if you had a function call instead of a constant you would see the function get called multiple times. – Scott Chamberlain Sep 11 '17 at 16:59
  • @ScottChamberlain. Great point. Added it into my answer. – Yair Halberstadt Sep 11 '17 at 17:04
0

Others have posted that they are not the same, and they are correct. I'm going to go a little further in to some low level detail though, and show you the IL (Intermediate Language) code generated for each version by the compiler.

Public getter, private setter vs public getter

The public getter, and private setter:

public class Test1
{
    public int MyProperty { get; private set; }
}

Generates:

Test1.get_MyProperty:
IL_0000:  ldarg.0     
IL_0001:  ldfld       UserQuery+Test1.<MyProperty>k__BackingField
IL_0006:  ret         

Test1.set_MyProperty:
IL_0000:  ldarg.0     
IL_0001:  ldarg.1     
IL_0002:  stfld       UserQuery+Test1.<MyProperty>k__BackingField
IL_0007:  ret         

Test1..ctor:
IL_0000:  ldarg.0     
IL_0001:  call        System.Object..ctor
IL_0006:  nop         
IL_0007:  ret  

The version with only the getter:

public class Test2
{
    public int MyProperty { get; }
}

Generates:

Test2.get_MyProperty:
IL_0000:  ldarg.0     
IL_0001:  ldfld       UserQuery+Test2.<MyProperty>k__BackingField
IL_0006:  ret         

Test2..ctor:
IL_0000:  ldarg.0     
IL_0001:  call        System.Object..ctor
IL_0006:  nop         
IL_0007:  ret  

Public getter vs Expression-Body getter

Getter only:

public class Test1
{
    public int MyProperty => 123;
}

Generates:

Test1.get_MyProperty:
IL_0000:  ldc.i4.s    7B 
IL_0002:  ret         

Test1..ctor:
IL_0000:  ldarg.0     
IL_0001:  call        System.Object..ctor
IL_0006:  nop         
IL_0007:  ret  

Expression-Body getter:

public class Test2
{
    public int MyProperty { get; } = 123;
}

Generates:

Test2.get_MyProperty:
IL_0000:  ldarg.0     
IL_0001:  ldfld       UserQuery+Test2.<MyProperty>k__BackingField
IL_0006:  ret         

Test2..ctor:
IL_0000:  ldarg.0     
IL_0001:  ldc.i4.s    7B 
IL_0003:  stfld       UserQuery+Test2.<MyProperty>k__BackingField
IL_0008:  ldarg.0     
IL_0009:  call        System.Object..ctor
IL_000E:  nop         
IL_000F:  ret  

As you can see, all 4 of these generate very different results behind the scenes at the IL level.

Depending on the compiler optimization settings, these could be different. The compile could choose to remove the backing field if it sees nothing uses it. It could also do the same for an unused private setter.

It is also possible that they may be jitted (just-int-time compilation) differently as the run-time processes them.

Bradley Uffner
  • 16,641
  • 3
  • 39
  • 76