This example shows how to create an immutable lightweight class that serves only to encapsulate a set of automatically implemented properties. Use this kind of construct instead of a struct when you must use reference type semantics.
You can make an immutable property in the following ways:
- Declare only the get accessor, which makes the property immutable everywhere except in the type's constructor.
- Declare an init accessor instead of a
set
accessor, which makes the property settable only in the constructor or by using an object initializer.
- Declare the set accessor to be private. The property is settable within the type, but it's immutable to consumers.
You can add the required
modifier to the property declaration to force callers to set the property as part of initializing a new object.
The following example shows how a property with only get accessor differs than one with get and private set.
class Contact
{
public string Name { get; }
public string Address { get; private set; }
public Contact(string contactName, string contactAddress)
{
Name = contactName;
Address = contactAddress;
}
public void ChangeAddress(string newAddress) => Address = newAddress;
}
The following example shows two ways to implement an immutable class that has automatically implemented properties. Each way declares one of the properties with a private set
and one of the properties with a get
only. The first class uses a constructor only to initialize the properties, and the second class uses a static factory method that calls a constructor.
class Contact
{
public string Name { get; }
public string Address { get; private set; }
public Contact(string contactName, string contactAddress)
{
Name = contactName;
Address = contactAddress;
}
}
public class Contact2
{
public string Name { get; private set; }
public string Address { get; }
private Contact2(string contactName, string contactAddress)
{
Name = contactName;
Address = contactAddress;
}
public static Contact2 CreateContact(string name, string address)
{
return new Contact2(name, address);
}
}
public class Program
{
static void Main()
{
string[] names = ["Terry Adams","Fadi Fakhouri", "Hanying Feng",
"Cesar Garcia", "Debra Garcia"];
string[] addresses = ["123 Main St.", "345 Cypress Ave.", "678 1st Ave",
"12 108th St.", "89 E. 42nd St."];
var query1 = from i in Enumerable.Range(0, 5)
select new Contact(names[i], addresses[i]);
var list = query1.ToList();
foreach (var contact in list)
{
Console.WriteLine("{0}, {1}", contact.Name, contact.Address);
}
var query2 = from i in Enumerable.Range(0, 5)
select Contact2.CreateContact(names[i], addresses[i]);
var list2 = query2.ToList();
}
}
The compiler creates backing fields for each automatically implemented property. The fields aren't accessible directly from source code.