Properties (C# Programming Guide)
A property is a member that provides a flexible mechanism to read, write, or compute the value of a data field. Properties appear as public data members, but they're implemented as special methods called accessors. This feature enables callers to access data easily and still helps promote data safety and flexibility. The syntax for properties is a natural extension to fields. A field defines a storage location:
public class Person
{
public string? FirstName;
// Omitted for brevity.
}
Automatically implemented properties
A property definition contains declarations for a get
and set
accessor that retrieves and assigns the value of that property:
public class Person
{
public string? FirstName { get; set; }
// Omitted for brevity.
}
The preceding example shows an automatically implemented property. The compiler generates a hidden backing field for the property. The compiler also implements the body of the get
and set
accessors. Any attributes are applied to the automatically implemented property. You can apply the attribute to the compiler-generated backing field by specifying the field:
tag on the attribute.
You can initialize a property to a value other than the default by setting a value after the closing brace for the property. You might prefer the initial value for the FirstName
property to be the empty string rather than null
. You would specify that as shown in the following code:
public class Person
{
public string FirstName { get; set; } = string.Empty;
// Omitted for brevity.
}
Access control
The preceding examples showed read / write properties. You can also create read-only properties, or give different accessibility to the set and get accessors. Suppose that your Person
class should only enable changing the value of the FirstName
property from other methods in that class. You could give the set accessor private
accessibility instead of public
:
public class Person
{
public string? FirstName { get; private set; }
// Omitted for brevity.
}
The FirstName
property can be read from any code, but it can be assigned only from code in the Person
class.
You can add any restrictive access modifier to either the set or get accessors. An access modifier on an individual accessor must be more restrictive than the access of the property. The preceding code is legal because the FirstName
property is public
, but the set accessor is private
. You couldn't declare a private
property with a public
accessor. Property declarations can also be declared protected
, internal
, protected internal
, or, even private
.
There are two special access modifiers for set
accessors:
- A
set
accessor can haveinit
as its access modifier. Thatset
accessor can be called only from an object initializer or the type's constructors. It's more restrictive thanprivate
on theset
accessor. - An automatically implemented property can declare a
get
accessor without aset
accessor. In that case, the compiler allows theset
accessor to be called only from the type's constructors. It's more restrictive than theinit
accessor on theset
accessor.
Modify the Person
class so as follows:
public class Person
{
public Person(string firstName) => FirstName = firstName;
public string FirstName { get; }
// Omitted for brevity.
}
The preceding example requires callers to use the constructor that includes the FirstName
parameter. Callers can't use object initializers to assign a value to the property. To support initializers, you can make the set
accessor an init
accessor, as shown in the following code:
public class Person
{
public Person() { }
public Person(string firstName) => FirstName = firstName;
public string? FirstName { get; init; }
// Omitted for brevity.
}
These modifiers are often used with the required
modifier to force proper initialization.
Required properties
The preceding example allows a caller to create a Person
using the default constructor, without setting the FirstName
property. The property changed type to a nullable string. Beginning in C# 11, you can require callers to set a property:
public class Person
{
public Person() { }
[SetsRequiredMembers]
public Person(string firstName) => FirstName = firstName;
public required string FirstName { get; init; }
// Omitted for brevity.
}
The preceding code makes two changes to the Person
class. First, the FirstName
property declaration includes the required
modifier. That means any code that creates a new Person
must set this property using an object initializer. Second, the constructor that takes a firstName
parameter has the System.Diagnostics.CodeAnalysis.SetsRequiredMembersAttribute attribute. This attribute informs the compiler that this constructor sets all required
members. Callers using this constructor aren't required to set required
properties with an object initializer.
Important
Don't confuse required
with non-nullable. It's valid to set a required
property to null
or default
. If the type is non-nullable, such as string
in these examples, the compiler issues a warning.
var aPerson = new Person("John");
aPerson = new Person{ FirstName = "John"};
// Error CS9035: Required member `Person.FirstName` must be set:
//aPerson2 = new Person();
Expression body definitions
Property accessors often consist of single-line statements. The accessors assign or return the result of an expression. You can implement these properties as expression-bodied members. Expression body definitions consist of the =>
token followed by the expression to assign to or retrieve from the property.
Read-only properties can implement the get
accessor as an expression-bodied member. The following example implements the read-only Name
property as an expression-bodied member:
public class Person
{
public Person() { }
[SetsRequiredMembers]
public Person(string firstName, string lastName)
{
FirstName = firstName;
LastName = lastName;
}
public required string FirstName { get; init; }
public required string LastName { get; init; }
public string Name => $"{FirstName} {LastName}";
// Omitted for brevity.
}
The Name
property is a computed property. There's no backing field for Name
. The property computes it each time.
Properties with backing fields
You can mix the concept of a computed property with a private field and create a cached evaluated property. For example, update the FullName
property so that the string formatting happens on the first access:
public class Person
{
public Person() { }
[SetsRequiredMembers]
public Person(string firstName, string lastName)
{
FirstName = firstName;
LastName = lastName;
}
public required string FirstName { get; init; }
public required string LastName { get; init; }
private string? _fullName;
public string FullName
{
get
{
if (_fullName is null)
_fullName = $"{FirstName} {LastName}";
return _fullName;
}
}
}
This implementation works because the FirstName
and LastName
properties are readonly. People can change their name. Updating the FirstName
and LastName
properties to allow set
accessors requires you to invalidate any cached value for fullName
. You modify the set
accessors of the FirstName
and LastName
property so the fullName
field is calculated again:
public class Person
{
private string? _firstName;
public string? FirstName
{
get => _firstName;
set
{
_firstName = value;
_fullName = null;
}
}
private string? _lastName;
public string? LastName
{
get => _lastName;
set
{
_lastName = value;
_fullName = null;
}
}
private string? _fullName;
public string FullName
{
get
{
if (_fullName is null)
_fullName = $"{FirstName} {LastName}";
return _fullName;
}
}
}
This final version evaluates the FullName
property only when needed. The previously calculated version is used if valid. Otherwise, the calculation updates the cached value. Developers using this class don't need to know the details of the implementation. None of these internal changes affect the use of the Person object.
Beginning with C# 13, you can create partial
properties in partial
classes. The implementing declaration for a partial
property can't be an automatically implemented property. An automatically implemented property uses the same syntax as a declaring partial property declaration.
Properties
Properties are a form of smart fields in a class or object. From outside the object, they appear like fields in the object. However, properties can be implemented using the full palette of C# functionality. You can provide validation, different accessibility, lazy evaluation, or any requirements your scenarios need.
- Simple properties that require no custom accessor code can be implemented either as expression body definitions or as automatically implemented properties.
- Properties enable a class to expose a public way of getting and setting values, while hiding implementation or verification code.
- A get property accessor is used to return the property value, and a set property accessor is used to assign a new value. An init property accessor is used to assign a new value only during object construction. These accessors can have different access levels. For more information, see Restricting Accessor Accessibility.
- The value keyword is used to define the value the
set
orinit
accessor is assigning. - Properties can be read-write (they have both a
get
and aset
accessor), read-only (they have aget
accessor but noset
accessor), or write-only (they have aset
accessor, but noget
accessor). Write-only properties are rare.
C# Language Specification
For more information, see Properties in the C# Language Specification. The language specification is the definitive source for C# syntax and usage.