Properties

Learn to control access to class fields using properties.

In C#, there are regular and special methods that control access to class fields. These methods are called properties.

Define a property

Properties are created with the following syntax:

// Like other members, properties can have acess modifiers
[access modifiers] property_type property_name
{
    get
    {
        // Get block contains actions to perform when returning the value of the field
    }
    
    set
    {
        // Set block contains actions to perform when setting the value of the field
    }
}

Properties don’t hold any value. They only act as intermediaries between the external code and the fields.

Let’s look at an example.

C#
using System;
namespace Properties
{
class Program
{
static void Main(string[] args)
{
Car car = new Car();
car.Model = "Lexus LS 460 L";
car.Price = 65000;
Console.Write($"{car.Model} costs {car.Price} dollars");
}
}
}

Here, the Car class defines the private model and price fields that store the model and the price of the car. Along with these fields, there are two public properties called Model and Price.

Property Naming

Property usage

Let’s take the model field, for example. With the help of the Model property, we can control the access to that field:

get { return model; } // Returns the value of the field

The get block returns the value of the field. Depending on the requirements, it can perform checks or other manipulations before returning the data.

set { model = value; } // Sets the value of the field

We set the value of the model field in the set block. The value is a placeholder for the value that’s passed to the property:

car.Model = "Lexus LS 460L"; // value

We can work with properties just like with regular fields. When we assign a value to a property, the set block is triggered. When we read the value of a property, the get block is triggered.

What’s the use of properties if they only pass values back and forth?

Validate values with properties

Properties have the capacity to control the access. In our example, the price field shouldn’t have a negative value, because price can’t be negative. We can prevent users from setting a negative value with the help of a property:

private decimal price;
public decimal Price
{
    get { return price; }
    set
    {
        if (price < 0)
        {
            price = 0;
        }
        else
        {
            price = value;
        }
    }
}

Now, if the user sets a negative number to the Price property, price will be zero and therefore won’t be negative.

Properties and Other Types

Access modifiers

We can control access to a property using access modifiers. We can decorate the whole property or its separate blocks:

public string Model
{
    get { return model; }
    private set { model = value; }
}

In the example above, the property itself is public. We can apply more restrictive modifiers to individual blocks.

Making the set block private makes it impossible for an external class to set the value of the property.

C#
using System;
namespace Properties
{
public class Car
{
private string model;
public string Model
{
get { return model; }
private set { model = value; }
}
private decimal price;
public decimal Price
{
get { return price; }
set
{
if (price < 0)
{
price = 0;
}
else
{
price = value;
}
}
}
public Car(string model)
{
// We can access the set block of the property
// only from within this class.
Model = model;
}
}
}

When setting access modifiers to set and get blocks, we must consider the following constraints:

  • We can’t set an access modifier explicitly if the property contains only one block (set or get):

    public string Name
    {
        private get; // Invalid, must also have the set block
    }
    
  • Only one block can have an access modifier at a time:

    public string Name
    {
        private get;
        private set; // Invalid, only one block can have an access modifier
    }
    
  • The access modifier of the set or get block must be more restrictive than the modifier attached to the property.

    internal string Name
    {
        public get; // Cannot be public, because the property is internal
        set;
    }
    

Auto-implemented properties

When we have many fields and don’t require complex access-control logic, we can use auto-implemented properties.

Consider the following example:

using System;

namespace Properties
{
    public class Car
    {
        private string model;
        public string Model
        {
            get { return model; }
            set { model = value; } 
        }

        private decimal price;
        public decimal Price
        {
            get { return price; }
            set { price = value; }
        }

        private int year;
        public int Year
        {
            get { return year; }
            set { year = value; }
        }
    }
}

Imagine that we have 20 more fields and we have to write 20 more properties that only pass the respective field value back and forth. That will quickly get tedious.

Auto-implemented properties were introduced to .NET to avoid this:

public string Model { get; set; }

This auto-property has a backing field that’s automatically generated by the compiler during compilation:

[CompilerGenerated]
private string <Model>k__BackingField;

public string Model
{
    [CompilerGenerated]
    get
    {
        return <Model>k__BackingField;
    }
    [CompilerGenerated]
    set
    {
        <Model>k__BackingField = value;
    }
}

The compiler also generates the respective get and set blocks.

The primary advantage of auto-properties is that they can be expanded at any moment if any additional logic is needed.

Access modifiers can be used in the same way as with regular properties:

public string Model { get; private set; }

If we make all properties auto-implemented, this would be our resulting Car class:

C#
using System;
namespace Properties
{
public class Car
{
// Auto-implemented properties
public string Model { get; set; }
public decimal Price { get; set; }
public int Year { get; set; }
}
}

Extra

.NET IDEs contain shortcuts because creating a property for a field is among the most common operations performed by software developers. For instance, in Visual Studio, we can automatically generate a property for a field by using the refactoring functionality:

Property generation
Property generation