Get up to 80 % extra points for free! More info:

Lesson 10 - Properties in C# .NET

In the previous exercise, Solved tasks for OOP in C# .NET lesson 9, we've practiced our knowledge from previous lessons.

In the previous lesson, Solved tasks for OOP in C# .NET lesson 9, we learned about static class members in C# .NET. In today's tutorial, we're going to look at the other class members that we haven't gone over yet.

Properties

We often want to have control over how an object field is being changed from outside of the class. We'd like to set the field as read-only or react to its changes somehow. Let's create a new project (name it Properties) and add the following Student class which will represent a student in a database.

class Student
{
    public string name;
    public bool male;
    public int age;
    public bool fullAged;

    public Student(string name, bool gender, int age)
    {
        this.name = name;
        this.male = gender;
        this.age = age;
        fullAged = true;
        if (age < 18)
            fullAged = false;
    }

    public override string ToString()
    {
        string iAmFullAged = "I'm";
        if (!fullAged)
            iAmFullAged = "I'm not";
        string gender = "male";
        if (!male)
            gender = "female";
        return String.Format("I'm {0}, {1}. I'm {2} years old and {3} of age.", name, gender, age, iAmFullAged);
    }

}

The class is very simple, the student has a name, gender, and age. The fullAged field is set according to the age and provides a more comfortable determination whether the student is of age from different places in the system. We use a bool value to store the gender, true indicates that he's male. The constructor will determine whether the student is of age or not. The ToString() method has been altered to suit our needs. In a real world situation, it'd probably just return the student's name. Let's create a student using the constructor:

Student s = new Student("Peter Brown", true, 20);
Console.WriteLine(s);

The output:

Console application
I'm Peter Brown, male. I'm 20 years old and I am of age.

Everything looks nice, but the fields can be re-written. We can break the object like this, so it'll no longer function properly (i.e. would have an inconsistent internal state):

Student s = new Student("Peter Brown", true, 20);
s.age = 15;
s.male = false;
Console.WriteLine(s);
Console.ReadKey();

The output:

Console application
I am Peter Brown, female. I am 15 years old and I am of age.

Certainly, we would want fullAged to be updated when the age is changed. Aside from that, no other field would need to be altered externally. Students don't usually change their genders or names.However, want to keep the properties accessible for reading, so we can't make them private. In earlier lessons of our course, we've used get methods to read private fields. We would name them something like GetAge() and so on. We'll create get methods to be able to read certain fields and make these fields private to prevent them from being modified from the outside. The class would now look something like this (I omitted the constructor and ToString()):

class Student
{
    private string name;
    private bool male;
    private int age;
    private bool fullAged;

    ...

    public string GetName()
    {
        return name;
    }

    public bool GetFullAged()
    {
        return fullAged;
    }

    public int GetAge()
    {
        return age;
    }

    public bool Male()
    {
        return male;
    }

    public void SetAge(int value)
    {
        age = value;
        // updating whether student is of age
        fullAged = true;
        if (age < 18)
            fullAged = false;
    }

}

The methods just returning a value are very simple. In the method setting the age is some more logic, since we have to reconsider the fullAged field. We made sure we can't set variables in any other way than what we want. We now control all the field changes and can work with them as needed. Our design must prevent all unwanted changes of the internal state that would cause an object to malfunction.

Methods for returning values are called getters and methods for setting values are called setters. We could potentially add an EditStudent() method to edit the other fields sort of like the constructor. Essentially, the student's name, age, and other fields would be updated using this method. We could also validate the values being set there since we would be able to handle all attempts to change certain values in one place. Implementing getters and setters manually is without a doubt hard work. Couldn't someone just do it for us? Yep, C# is able to generate them for us! In that case, we're no longer talking about fields, rather properties.

The property syntax is very similar to the field syntax:

public string Name { get; set; }

At first, it may seem as if we had declared a field. The property name is capitalized because it is actually a method (2 methods to be precise). In curly brackets, we would specify the methods we wish to generate. We don't write a semicolon after properties! In the example above, both the setter and the getter would be generated, and the property would be accessible both for reading and writing:

Console.WriteLine(object.Name); // reading
object.Name = "John Black"; // writing

From the outside, the only significant difference against an attribute is that the first letter is uppercase. C# internally generates a private field and two public methods that are called automatically depending on the context (whether we read or write the value). If we didn't generate the setter part of the property, there would be no way to change the property from the inside nor the outside. If you want to prevent all alteration of your variable from outside the class, you would just make its setter private:

public string Name { get; private set; }

We'll use this very often and most of the properties in our classes will look like this from now on.

If you wish the getter or the setter to include more functionality, not just reading or writing values, you could define it manually. Let's take our example of the full-aged problem which must be re-evaluated when the student's age changes:

private int age;
public int Age
{
    get
    {
        return age;
    }
    set
    {
        age = value;
        // updating whether student is of age
        fullAged = true;
        if (age < 18)
            fullAged = false;
    }
}

First and foremost, we'll have to create a private age field that starts with a lowercase letter, the value will be stored there. We'll work with this field in the getter and the setter. If you were to use "Age", with a capital a, in the getter or setter, the program would get stuck in an infinite loop! Why? Well, take another look at the code above (hint: the method we are currently in has already been declared as that). You cannot implement a custom getter and let the setter be generated automatically. They have to either both be generated automatically or both be implemented manually. To access the value being set in the setter, we use the value keyword. All properties had to be implemented like this until C# added auto-implemented properties in version 3.0. As a matter of fact, we don't need any logic at all in the most of the properties. We'll treat the Age as we would treat a field from now (remember it is case sensitive). Re-assignment of the "Age" triggers the internal logic to update the fullAged field:

object.Age = 15; // the fullAged field will update immediately as well

Likewise, we could implement a custom getter and log something.

Let's update our Student class so it'll use properties:

class Student
{
    public string Name { get; private set; }
    public bool Male { get; private set; }
    public bool FullAged { get; private set; }
    private int age;
    public int Age
    {
        get
        {
            return age;
        }
        set
        {
            age = value;
            // updating whether student is of age
            FullAged = true;
            if (age < 18)
                FullAged = false;
        }
    }

    public Student(string name, bool gender, int age)
    {
        EditStudent(name, gender, age);
    }

    public void EditStudent(string name, bool gender, int age)
    {
        Name = name;
        Male = gender;
        Age = age;
    }

    public override string ToString()
    {
        string iAmFullAged = "I am";
        if (!FullAged)
            iAmFullAged = "I am not";
        string gender = "male";
        if (!Male)
            gender = "female";
        return String.Format("I am {0}, {1}. I am {2} years old and {3} of age.", Name, gender, Age, iAmFullAged);
    }
}

Seems much better now, right? From now on, we will always use properties rather than fields since they allow us to encapsulate objects perfectly. In the .NET framework, all public class members are properties. For example, the Length property of a String. There's a general design guideline that we follow that states: values that are allowed to exit a class must be properties, and values that are internal and "non-editable", must be private fields. Overall, we don't use public fields. The whole class and demo app are, of course, available for download below the article. We can now remove the fullAged checking from the constructor since we now set the age using the Age property, and the FullAged property is updated automatically. Let's try the code which caused issues earlier:

Student s = new Student("Peter Brown", true, 20);
s.Age = 15;
//s.Male = false; // This line now causes an error and has to be removed
Console.WriteLine(s);

The output:

Console application
I am Peter Brown, male. I am 15 years old and I am not of age.

If we set the entire property as private, setters and getters will not be able to be marked as public.

In the next lesson, Date and time in C# .NET, we'll learn, how to work with date and time in .NET.


 

Did you have a problem with anything? Download the sample application below and compare it with your project, you will find the error easily.

Download

By downloading the following file, you agree to the license terms

Downloaded 25x (38.67 kB)
Application includes source codes in language C#

 

Previous article
Solved tasks for OOP in C# .NET lesson 9
All articles in this section
Object-Oriented Programming in C# .NET
Skip article
(not recommended)
Date and time in C# .NET
Article has been written for you by David Capka Hartinger
Avatar
User rating:
4 votes
The author is a programmer, who likes web technologies and being the lead/chief article writer at ICT.social. He shares his knowledge with the community and is always looking to improve. He believes that anyone can do what they set their mind to.
Unicorn university David learned IT at the Unicorn University - a prestigious college providing education on IT and economics.
Activities