This article is currently in the process of being translated into German (~66% done).
Eigenschaften
Im vorherigen Artikel haben wir Felder diskutiert. Sie sind wie eine globale Variable für eine Klasse, welche dir Zugang erlaubt von allen Methoden aus. Wir haben auch kurz über die Felder diskutiert, welche von anderen Klassen aus zugegriffen werden können wenn diese als public (=öffentlich) markiert sind, das zu tun wird jedoch nicht empfohlen. Für Variablen/Felder auf welche du von außerhalb deiner Klasse zugreifen willst kannst du stattdessen properties benutzen.
Wenn du ein Feld als öffentlich bezeichnest, ermöglichst du den kompletten Zugriff auf dieses Feld von außen - andere Klassen können damit tun was immer sie wollen, ohne die zugewiesene Klasse zu beachten. Eigenschaften - sogenannte ,,properties" - geben die Kontrolle an die zugewiesene Klasse wieder zurück, indem ein Feld als nur "lesbar"- oder "schreibar" spezifiziert wird, und ermöglicht dann der deklarierenden Klasse, den Wert zuvor zu überprüfen und zu manipulieren, bevor der Wert zurückgegeben oder dem Feld zugeordnet wird.
Eine Eigenschaft ist ein wenig wie eine Mischung aus einem Feld und einer Methode, weil es sehr ähnlich wie ein Feld deklariert wird mit der Sichtbarkeit, einem Datentyp und einem Namen, aber hat auch einen Körper wie eine Methode um das Verhalten zu steuern:
public string Name
{
get { return _name; }
set { _name = value; }
}
Beachten Sie die speziellen get und set Schlüsselwörter. Sie werden explizit für Eigenschaften benutzt, um das Verhalten beim lesen (get'ing) und schreiben (set'ing) eines Feldes zu kontrollieren. Du kannst Eigenschaften auch nur mit einer get-Implementation ODER einer set-Implementation haben, um Eigenschaften dementsprechend Schreibgeschützt oder Lesegeschützt zu kreieren. Zusätzlich kannst du auch die Visibilität der get- und set-Implementationen kontrollieren, zum Beispiel kann die Eigenschaft von überall gelesen (public), aber nur in der eigenen Klasse modifiziert werden (private).
Du wirst außerdem bemerken, dass ich auf ein Feld namens _name referenziere. Dieses wirst du in deiner Klasse auch deklarieren müssen, sodass deine Property es benutzen kann. Ein gebräuchliches Muster für Felder und Properties sieht folgendermaßen aus.:
private string _name = "John Doe";
public string Name
{
get { return _name; }
set { _name = value; }
}
Nun kannst du sehen wie das Feld und die Eigenschaft zusammenarbeiten. Die get Methode gibt den Wert des _name Feldes zurück, während die set Methode den übergebenen Wert dem _name Feld zuweist. In der Set- Methode verwenden wir gewöhnlich das besondere Schlüsselwort value, welches in dieser besonderen Situation auf den Wert verweist, der dann die Eigenschaft übergeben wird.
Also, das ist im Prinzip so einfach wie es nur geht und an diesem Punkt haben wir noch nichts getan, was nicht auch mit einem einfachen öffentlichen Feld getan werden könnte. Aber an einem späteren Punkt könntest du entscheiden, dass du mehr Kontrolle darüber nehmen möchtest, wie andere Klassen mit dem Namen arbeiten können. Und da du dies als Eigenschaft eingebaut hast, kannst du diese Implementierung ohne Probleme modifizieren, ohne jemanden, der deine Klasse benutzt, dabei zu stören. Zum Beispiel kann die Name Eigenschaft verändert werden, um so auszusehen:
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;
}
}
Die getter-Methode erzwingt jetzt, dass der Rückgabewert immer in GROSSBUCHSTABEN geliefert wird, unabhängig davon, ob im zugrundeliegenden Feld (_name) Großbuchstaben und / oder Kleinbuchstaben verwendet wurden. In der setter-Methode haben wir einige Codezeilen hinzugefügt, um zu überprüfen, ob der übergebene Wert ein Leerzeichen enthält, denn wir haben entschieden, dass der Name immer aus beidem, einen Vornamen und einem Nachnamen bestehen soll. Falls dies nicht der Fall ist, wird eine Exception geworfen. Das alles ist sehr grob und vereinfacht, aber es soll aufzeigen, dass man mit der Verwendung von Eigenschaften die vollständige Kontrolle über das Geschehen bekommt.
Read-only properties
Die meisten Eigenschaften, die du in diesem Tutorial sehen wirst, können sowohl gelesen als auch überschrieben werden, da dies die häufigsten Verwendungen von Eigenschaften sind. Aber das muss nicht immer so sein. Du kannst eine Eigenschaft auch erstmal nur mit einer get-Methode deklarieren, etwa so:
private string _name = "John Doe";
public string Name
{
get { return _name; }
}
In diesem Fall kann die Eigenschaft "Namen" nicht mehr verändert werden, sie kann nur mehr gelesen werden und der Compiler wird eine Fehlermeldung werfen, wenn versucht wird, einen Wert zuzuweisen. Allerdings kann der Wert immer noch von innerhalb der Klasse verändert werden, denn hier (innerhalb der Klasse) kann man dem zugrundeliegenden Feld "_name" einfach einen neuen Wert zuweisen. Allerdings übergeht man damit gewissermaßen einen der größten Vorteile von Eigenschaften: Die Fähigkeit, jederzeit die Kontrolle darüber zu behalten ob ein wert akzeptiert wird. Wie wir bereits besprochen haben, ist die setter-Methode eine großartige Möglichkeit die Validierung (Gültigkeitsprüfung bzw. Plausibilitätsprüfung) des Wertes durchzuführen, aber wenn dem Feld "_name" von verschiedenen Orten ein neuer Wert zugewiesen wird, weil die Eigenschaft eigentlich nur dem lesenden Zugriff vorbehalten ist, kommt es nicht mehr zu dieser Validierung (sie wird umgangen).
Glücklicherweise bietet uns c# eine Lösung dafür: Man kann eine setter - Methode definieren, aber deren Sichtbarkeit limitieren, beispielsweise durch Verwendung der Schlüsselwörter private oder protected. Damit bekommt man das beste von beiden Welten: Einerseits hat man weiterhin die Möglichkeit, der Eigenschaft von innerhalb der Klasse Werte zuzuweisen (oder auch von jeder abgeleiteten Klasse, wenn das Schlüsselwort protected verwendet wurde), andererseits wird dieses auch entsprechend geprüft bzw. validiert. Das folgende Beispiel illustriert dies:
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");
}
Der wesentliche Unterschied hier ist einfach das Schlüsselwort "private" direkt vor dem Schlüsselwort "set", und wie bereits erwähnt, kann dieses auch - je nach Bedarf - z.B. durch protected oder internal ersetzt werden.
Automatisch implementierte Eigenschaften
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.
Zusammenfassung
Eigenschaften (properties) geben uns mehr Kontrolle darüber, wie auf ein Feld zugegriffen und wie es manipuliert werden kann. Sie sollten immer dann verwendet werden, wenn der Zugriff von außerhalb der sie deklarierenden Klasse erfolgen soll.