TOC

This article is currently in the process of being translated into Dutch (~36% done).

Klassen:

Eigenschappen

In het vorige artikel bespraken we velden(fields). Deze lijken op globale variabelen voor een klasse (class) en je kunt ze daarom aanspreken vanuit alle methoden. We hebben ook kort besproken dat velden KUNNEN worden aangesproken vanuit andere klassen zolang ze gemarkeerd zijn als publiek (public), alhoewel dit over het algemeen niet wordt aangeraden. Om variabelen/velden aan te spreken buiten je klasse hoor je in plaats daarvan eigenschappen (properties) gebruiken.

Wanneer je een veld declareert als publiek, geef je er complete toegang tot van buiten, oftewel andere klassen kunnen er mee doen wat ze willen, zonder dat aan klasse die het veld declareert aan te geven. Eigenschappen geven de controle terug aan de declarerende klasse door te specificeren of een veld enkel lezen of schrijven toestaat en zelfs door de declarerende klasse toe te staan de waarde te controleren en te veranderen voordat deze weer wordt teruggeplaatst in of toegeschreven aan het veld.

Een eigenschap lijkt een beetje op een combinatie tussen een veld en een methode, omdat eigenschappen op een manier worden gedeclareerd die lijkt op het declareren van een veld met: zichtbaarheid, datatype en naam. Maar een eigenschap heeft ook een lichaam (body), zoals een methode, om het gedrag te bepalen:

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

Let op de speciale get en set sleutelwoorden. Ze worden alleen gebruikt voor eigenschappen, om het gedrag tijdens het lezen (get'ing) en schrijven (set'ing) van het veld te bepalen. Je kunt eigenschappen hebben met enkel een "get" of "set" implementatie. Daarmee maak je eigenschappen die enkel lees of schrijf zijn, ook kun je de zichtbaarheid van de get en set implementaties bepalen, bijv. om een eigenschap te maken die vanaf overal kan worden gelezen (publiek/public), maar enkel kan worden aangepast vanuit de declarerende klasse(prive/private).

Het zal je ook opvallen dat ik refereer aan een veld genaamd "_name". Dit veld zul je ook in je klasse moeten declareren, zodat je eigenschap het kan gebruiken. Een gebruikelijk patroon voor het maken van velden en eigenschappen ziet er als volgt uit:

private string _name = "John Doe";

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

Nu kun je zien hoe het veld en de eigenschap samenwerken: De get methode zal de waarde van het _name veld teruggeven, terwijl de set methode de doorgegeven waarde zal toekennen aan het _name veld. In de set methode gebruiken we het speciale sleutelwoord "value" dat in deze specifieke situatie refereert naar de waarde die wordt doorgegeven aan de eigenschap.

Zo, dit is ongeveer zo simpel als het wordt en tot dit punt doen we niets dat niet ook gedaan had kunnen worden door een simpel "public" veld. Maar in een later stadium beslis je misschien dat je meer controle wil nemen over hoe andere klassen werken met deze naam. En omdat je dit als een eigenschap hebt geimplementeerd, ben je nu vrij om de implementatie aan te passen zonder iemand anders in de weg te zitten die je klasse gebruikt. Bijvoorbeeld, de "Name" eigenschap kan worden aangepast om er zo uit te zien:

private string _name = "John Doe";

public string Name
{
	get 
	{
		return _name.ToUpper();
	}
	set 
	{
		if(!value.Contains(" "))
			throw new Exception("Please specify both first and last name!");
		_name = value; 
	}
}

The get method now enforces that the value returned is always in UPPERCASE, no matter which case the backing field (_name) is in. In the set method, we have added a couple of lines of code to check whether the passed value contains a space, because we have decided that the name should always consist of both a first and a last name - if this is not the case, an exception is thrown. This is all very crude and simplified, but it should illustrate the full level of control you get when using properties.

Read-only properties

Most properties you'll see in the examples of this tutorial will be both readable and writeable, because that's the most common usage of properties, but it doesn't always have to be like that. First of all, you can declare a property with only a get-method, like this:

private string _name = "John Doe";

public string Name
{
	get { return _name; }
}

In this case, you can no longer change the "Name" property - you can only read it and the compiler will throw an error if you try to assign a value to it. You can still change the value of it from inside the class though, since you can simply assign a new value to the backing field "_name". Doing it that way kind of negates one of the biggest advantage to properties though: The ability to always control whether a value can be accepted. As we already talked about, the set-method is a great way to perform validation of the value, but if you assign a new value to the _name field from multiple places, because the property is read-only, you don't get this validation.

Fortunately for us, C# offers a solution to this: You can define a set method on the property, but limit its visibility, using e.g. the private or the protected keyword. This will give you the best of both worlds, where you can still assign a value to the property from inside the class (or any inherited class if you use the protected keyword) and have it validated accordingly. Here's an example:

private string _name = "John Doe";

public string Name
{
	get { return _name; }

	private set
	{
		if(IsValidName(value))
			this._name = value;
	}
}

public bool IsValidName(string name)
{
	return name.EndsWith("Doe");

}

The key difference here is simply the "private" keyword right in front of the "set" keyword, and as mentioned, you can replace it with e.g. protected or internal, depending on your needs.

Auto-implemented properties

In some cases, you don't need all the control over a field and it can feel cumbersome to implement both a field and a property with the get and set methods not doing anything besides what we saw in the first example. You may be tempted to simply declare your variable as a public field to avoid all this extra hassle. But don't do that! Fortunately for all of us, Microsoft decided to add auto-implemented properties in C# version 3, which will save you several lines of code. Just consider the difference:

Regular property with a declared backing field:

private string _name;

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

The exact same behavior, but with an auto-implemented property:

public string Name { get; set; }

Notice that the get and set methods are empty and that no private backing field is declared - in other words, we can now accomplish the exact same behavior as in the first example but with a single line of code! Bear in mind that the private backing field will still exist at runtime - it will be auto-implemented by the compiler, as the name implies. Should you later decide that you need more control of this specific property, you can simply change it to a regular field/property combination with the desired implementation of the get and set methods.

Notice that you are still left with an important mechanism of control from regular properties when using auto-implemented properties: You can leave out the set keyword to create a read-only property, e.g. like this:

public string ReadOnlyProperty { get; }

Write-only properties are not allowed when using auto-implemented properties.

Auto-implemented properties with default values

Prior to C# version 6, you could not define a default value for an auto-implemented property - for that, you would need a declared backing field, which would allow you to initialize the variable with a value:

private string _name = "John Doe";

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

But in C# version 6, Microsoft finally added the ability to initialize an auto-implemented property with a default value, like this:

public string Name { get; set; } = "John Doe";

Expression-bodied properties

Another property-related feature Microsoft implemented in C# 6.0 and 7.0 is the expression bodied members. It simply allows you to write single-line expressions for your properties and methods - in this case, let's look at how to use it for your get/set methods in a way that takes up less space and requires slightly less typing:

private string name;
public string Name
{
	get => name;
	set => name = value;
}

If your property is read-only, the syntax can be even shorter:

public string Name => "John Doe";

Of course this also works if you need to actually do something before returning the value, like this:

public string Name { get; set; } = "John Doe";

public string FirstName => this.Name.Substring(0, this.Name.IndexOf(" "));

As you can see, this allows you to define a get method but without the actual get and return keywords, while encouraging you to keep it all in one line instead of multiple lines.

Summary

Properties gives your classes more control over how fields can be accessed and manipulated, and they should always be used when you want to give access to fields from outside of the declaring class.


This article has been fully translated into the following languages: Is your preferred language not on the list? Click here to help us translate this article into your language!