Jeremy Lundy
Jeremy Lundy

Reputation: 23

Is there a difference between a property initializer and an expression for simple values in C#?

Is there a difference between these two properties in C#? Would the first property be instantiated one time only while the second property would actually instantiate a new string object every time the property is accessed?

public string Property1 { get; } = "Property1";
public string Property2 => "Property2";

Upvotes: 2

Views: 98

Answers (2)

theB
theB

Reputation: 6738

To add some information to the other answer, when you compile a class with the two properties:

public string ReadOnlyInitialized { get; } = "String A";
public string ReadOnlyLambda => "String B";

The following IL is generated for the ReadOnlyInitialized property

.field private initonly string '<ReadOnlyInitialized>k__BackingField'
// snipped debugger attributes    

.property instance string ReadOnlyInitialized()
{
  .get instance string ScratchCsConsole.Program::get_ReadOnlyInitialized()
} // end of property Program::ReadOnlyInitialized

.method public hidebysig specialname instance string 
        get_ReadOnlyInitialized() cil managed
{
  // Code size       7 (0x7)
  .maxstack  8
  IL_0000:  ldarg.0
  IL_0001:  ldfld      string ScratchCsConsole.Program::'<ReadOnlyInitialized>k__BackingField'
  IL_0006:  ret
} // end of method Program::get_ReadOnlyInitialized

You can see from the Generated IL that the compiler generates a backing field for you.

This IL is generated for the ReadOnlyLambda property:

.property instance string ReadOnlyLambda()
{
  .get instance string ScratchCsConsole.Program::get_ReadOnlyLambda()
} // end of property Program::ReadOnlyLambda
.method public hidebysig specialname instance string 
        get_ReadOnlyLambda() cil managed
{
  // Code size       6 (0x6)
  .maxstack  8
  IL_0000:  ldstr      "String B"
  IL_0005:  ret
} // end of method Program::get_ReadOnlyLambda

As you can see with this version, the only difference is that the string is now being loaded onto the stack as an immediate, rather than loading from the backing field.

Upvotes: 0

Jeroen Mostert
Jeroen Mostert

Reputation: 28769

Yes, there is a difference. The first one declares a read-only property:

public string Property1 { get; } = "Property1";

This is shorthand/syntactic sugar for

private readonly string $property1 = "Property1";
public string Property1 { get { return $property1; } }

Where $property1 is some compiler-generated field.

The second one declares a property with a shorthand getter:

public string Property2 => "Property2";

Which is shorthand for:

public string Property2 { get { return "Property2"; } }

Which doesn't have a backing field at all.

The second one doesn't actually "instantiate a new string object" because strings are read-only and string constants are interned at compile time; it will simply return the same string instance every time.

In this particular case, you will hardly notice the difference since only things that explicitly look at the property with reflection are going to notice the presence or absence of a backing field. Things get more interesting with non-trivial getters that compute expressions:

private static string a = "a";
private static string b = "b";
public string Property1 { get; } = a + b;
public string Property2 => a + b;

Console.WriteLine(Property1 == Property2);   // true, since "ab" == "ab"
a = "no more a";
Console.WriteLine(Property1 == Property2);   // false, since "ab" != "no more ab"

(This is a contrived example, of course, but it shows the difference between a read-only property and a computed one.)

Upvotes: 6

Related Questions