Understanding C# Features (1) Auto Property
[LINQ via C#] - [C# Features]
As the fundamental of LINQ, This chapter will explain the new language features of C# 3.0, all of which are syntactic sugars.
Auto property
Before C# 3.0, a property has be with a getter/setter body:
public class Person { private string name; public string Name { get { return this.name; } set { this.name = value; } } }
It is annoying when a class has many properties for data. So C# 3.0+ supports auto property:
public class Person { public string Name { get; set; } }
which is a syntactic sugar. The compiler will generate the field definition and getter/setter body:
public class Person { [CompilerGenerated] private string nameBackingField; public string Name { [CompilerGenerated] get { return this.nameBackingField; } [CompilerGenerated] set { this.nameBackingField = value; } } }
Above 3 versions of Person class work the same. works the same as the first sample.
Getter only auto property
In programming, especially functional programming, it is a good practice to design atomic/immutable types:
public class Person { public Person(string name) { this.Name = name; } public string Name { get; private set; } }
C# 6.0 introduced more syntactic sugar to further simplify above code, so private setter can be omitted:
public class Person { public Person(string name) { this.Name = name; } public string Name { get; } }
For getter only auto property, compiler generates read only backing field. So getter only auto property can only be initialized from constructor, or from property initializer:
public class Person { public Person(string name) { this.Name = name; } public string Name { get; } public Guid Id { get; } = Guid.NewGuid(); }
Above code will be compiled to:
public class Person { [CompilerGenerated] private readonly string nameBackingField; [CompilerGenerated] private readonly Guid idBackingField = Guid.NewGuid(); public Person(string name) { this.nameBackingField = name; } public string Name { [CompilerGenerated] get { return this.nameBackingField; } } public Guid Id { [CompilerGenerated] get { return this.idBackingField; } } }
Auto property initializer
So getter only auto property can only be initialized from constructor, or from auto property initializer:
public class Person { public Person(string name) { this.Name = name; } public string Name { get; } public Guid Id { get; } = Guid.NewGuid(); }
Expression bodied property-like function member
Since C# 3.0, the following Person class can be defined:
public class Person { public Person(string firstName, string lastName) { this.FirstName = firstName; this.LastName = lastName; } public string FirstName { get; private set; } public string LastName { get; private set; } public string FullName { get { return string.Format(CultureInfo.InvariantCulture, "{0} {1}", this.FirstName, this.LastName); } } }
Since C# 6.0, the FirstName and LastName properties can be simplified to getter only, and FullName property can be simplified to expression body:
public class Person { public Person(string firstName, string lastName) { this.FirstName = firstName; this.LastName = lastName; } public string FirstName { get; } public string LastName { get; } public string FullName => $"{this.FirstName} {this.LastName}"; }
Please notice expression bodied property is different from auto property with initializer. Consider the following case:
public class Person { public Guid Id1 { get; } = Guid.NewGuid(); public Guid Id2 => Guid.NewGuid(); }
Every time when Id1 is called, it always returns the same GUID; Every time when Id2 is called, it always returns a new GUID. Actually, above class is compiled to:
public class Person { [CompilerGenerated] private readonly Guid id1BackingField = Guid.NewGuid(); public Guid Id1 { [CompilerGenerated] get { return this.id1BackingField; } } public Guid Id2 { [CompilerGenerated] get { return Guid.NewGuid(); } } }