C# Properties

  • C# properties are method-like members or the interfaces to manipulate the private fields instead of accessing the private fields directly.
  • A property can include a get accessor and a set accessor.
  • A get accessor is used to return the property value so it must include a return statement and the return type must be compatible with the property type.
  • A set accessor is used to assign a new value and the keyword value is the value which the property is assigned.
  • In C#, property is not a keyword and it is defined at least including one accessor, either get or set or both.

Example 01-72-01

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
using System;

class Salary
{
    float _hourlyRate;

    public float HourlyRate
    {
        // Define a get accessor
        get
        {
            return _hourlyRate;
        }

        // Define a set accessor
        set
        {
            _hourlyRate = value;
        }
    }

    public float WeeklyRate
    {
        get
        {
            return _hourlyRate * 40;
        }

        // The set accessor can be removed if you don't want to get hourly rate from weekly rate
        set
        {
            _hourlyRate = value / 40;
        }
    }
    
    static void Main()
    {
        Salary s = new Salary();
        s.HourlyRate = 20;  // HourlyRate set accessor is invoked
        Console.WriteLine("Hourly Rate: {0}", s.HourlyRate); // HourlyRate get accessor is invoked
        Console.WriteLine("Weekly Rate: {0}", s.WeeklyRate); // WeeklyRate get accessor is invoked

        Console.Read();
    }
}

Output

Hourly Rate: 20
Weekly Rate: 800

Explanation

  • Line 5: Declare a private field _hourlyRate to save the hourly rate of a salary.
  • Line 7-20: Declare a public property with get and set accessors.
  • Line 10-13: get accessor with a return statement and return type is float which is the same as that of the property.
  • Line 16-19: set accessor and the private field is assigned with value. If you regard "set" as a method, the parameter will be value with the property type and the return type of the method will be void. The hourly rate should be a positive number, so the set accessor can be enhanced as follows.
  •         set
            {
                if (value > 0)
                {
                    _hourlyRate = value;
                }
            }
    
  • Line 22-34: Declare a public property WeeklyRate with get and set accessors. The value can be computed and assigned or returned.
  • Line 39: When a property is assigned with a value, its set accessor will be invoked. Pay attention, a property is used like a field.
  • Line 40-41: get accessors are invoked to return the values of the properties.
  • Line 29-33: In this example, the set accessor of WeeklyRate has never been used so it can be removed.
Note Note
Conventionally a private field name is declared to start with a underscore and following with lower case characters but a property name or class name always starts with a capital letter.

The access modifier of properties can be public, private, protected, internal and protected internal and static, virtual and override keywords can be used in properties as well. By default, get and set accessors have the same access level as the property's but the access level can be changed individually if needed. The following rules must be followed to set the access modifier of a accessor.

  • The access modifier cannot be used on an interface because they are all public implicitly.
  • The access modifier can only be used on one of the accessor if the property has both a get and a set accessor.
  • The access modifier must match the modifier of the overridden accessor if any.
  • The access modifier must be restrictive than the access level of the property.

Example 01-72-02

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
using System;

class Base
{
    private string _name;

    public virtual string Name
    {
        protected get
        {
            return "Base:" + _name;
        }
        set
        {
            _name = value;
        }
    }
}

class Derived : Base
{
    private string _name;

    public override string Name
    {
        protected get
        {
            return "Derived:" + _name;
        }
        set
        {
            _name = value;
        }
    }

    public void printName()
    {
        Console.WriteLine(this.Name);
    }
}

class Test
{
    static void Main()
    {
        Derived d = new Derived();
        d.Name = "Test";
        d.printName();

        Console.Read();
    }
}

Output

Derived:Test

Explanation

  • Line 7-17: Declare a public virtual property in Base class.
  • Line 9-12: Declare a protected get accessor.
  • Line 13-16: Declare a set accessor without any access modifier. It's not allowed to declare access modifier in both accessors.
  • Line 24-34: Declare a public override property in Derived class.
  • Line 26-33: The modifier of the accessor can not be changed in an override property.
  • Line 36-39: Output the property's value.
  • Line 42-52: Testing by creating an object of Derived class, assigning "Test" to the property and output the property's value.

In the above example, the Base class can be defined as an abstract class and the abstract keyword is added in the property.

abstract class Base
{
    public abstract string Name
    {
        protected get;
        set;
    }
}

Because get or set accessor looks like a special method, properties can be defined in an interface with at least one accessor which does not have a body.

Example 01-72-03

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
using System;

interface IPerson
{
    string Name
    {
        get;
        set;
    }

    int NumberOfDogs
    {
        get;
    }
}

interface IDog
{
    string Name
    {
        get;
        set;
    }
}

public class PersonWithDogs : IPerson, IDog
{
    private string _dogName;

    string IDog.Name
    {
        get { return _dogName; }
        set { _dogName = value; }
    }

    string IPerson.Name
    {
        get { return "Mike"; }
        set { }
    }

    public int NumberOfDogs
    {
        get { return 1; }
    }
}

class Test
{
    static void Main()
    {
        PersonWithDogs p = new PersonWithDogs();
        IPerson ip = (IPerson)p;
        IDog id = (IDog)p;
        Console.WriteLine("My name is {0}.", ip.Name);
        Console.WriteLine("My dog name is {0}.", id.Name);
        Console.WriteLine("I have {0} dog{1}.", p.NumberOfDogs, p.NumberOfDogs > 1 ? "s" : "");

        Console.Read();
    }
}

Output

My name is Mike.
My dog name is .
I have 1 dog.

Explanation

  • Line 3-15: Declare an interface IPerson with 2 properties. The property Name defines 2 accessors without body and the property NumberOfDogs defines only get accessor which means the property is read-only.
  • Line 17-24: Declare an interface IDog. Pay attention, both IPerson and IDog have a property "Name".
  • Line 26-46: Declare a class implementing IPerson and IDog.
  • Line 30-34: Implement the property Name in IDog and the bodies of the accessors are added. IDog.Name is the fully qualified name of the property indicating the property is from IDog rather than IPerson.
  • Line 36-40: Implement the property Name in IPerson. The body of set accessor is empty and it means nothing will be happened if a value is assigned to the property.
  • Line 42-45: This is the normal implementation of the property NumberOfDogs and the get accessor must be implemented in its body. The interface name prefix is not needed.
  • Line 52-54: Instantiate an instance of class PersonWithDogs and assign it to the 2 interface variables by casting operators.
  • Line 55-56: Output person's name and dog's name. You cannot replace ip.Name or id.Name with p.Name because the property was implemented with the fully qualified name.
  • Line 57: This is normal call the get accessor in property NumberOfDogs.

When implementing a member with the same signature in more than 2 interfaces, the implementation is called Explicit Interface Implementation. The above example is a good explicit interface implementation for properties. If you want to see the example for methods, please check the C# interface page.

Example 01-72-04

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
using System;

class Student
{
    private string _name;
    private bool _isMale;
    private int _grade;

    public string Name
    {
        get { return _name; }
        set { _name = value; }
    }

    public bool IsMale
    {
        get { return _isMale; }
        set { _isMale = value; }
    }

    public int Grade
    {
        get { return _grade; }
        set { _grade = value; }
    }
}

class TestStudent
{
    static void Main()
    {
        Student s = new Student();
        s.Name = "Peter";
        s.IsMale = true;
        s.Grade = 3;
        Console.WriteLine("Name: {0}", s.Name);
        Console.WriteLine("Gender: {0}", s.IsMale ? "Male" : "Female");
        Console.WriteLine("Grade: {0}", s.Grade);

        Console.Read();
    }
}

Output

Name: Peter
Gender: Male
Grade: 3

The above is a regular property example with the access to private members only through the properties. Also there is no boundary check in the properties. In this case, we can omit the implementation of the properties and private members by using so called Auto-Implemented Properties. Actually when using auto-implemented properties, the CLR will take responsibility to create the private fields implicitly. The following is the version of auto-implemented properties of the example 01-99-04.

Example 01-72-05

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
using System;

class Student
{
    public string Name { get; set; }
    public bool IsMale { get; set; }
    public int Grade { get; set; }
}

class TestStudent
{
    static void Main()
    {
        Student s = new Student();
        s.Name = "Peter";
        s.IsMale = true;
        s.Grade = 3;
        Console.WriteLine("Name: {0}", s.Name);
        Console.WriteLine("Gender: {0}", s.IsMale ? "Male" : "Female");
        Console.WriteLine("Grade: {0}", s.Grade);

        Console.Read();
    }
}

Explanation

  • Line 5-7: Declare 3 auto-implemented properties instead of the regular properties in example 01-99-04.
  • As you can see, you got the same result but the code is short and clean.

I don't know if you noticed one thing or not. The auto-implemented property has the similar signature as that defined in an interface. Actually here is the difference.

  • An auto-implemented property is defined in a class instead of an interface.
  • An auto-implemented property can have an access modifier but an interface property has not.