Training - Beratung - Projektarbeiten

www.David-Tielke.de

Welches Feature ich mir in C# 7.0 wünschen würde

Heute habe ich ein Webinar zu Thema “C# 6.0” bei developer-media durchgeführt, welches in 90 Minuten alle wesentlichen Neuerungen in der sechsten Version, von Microsofts Entwicklungssprache Nr. 1, beleuchtete. Dabei stellte einer der Teilnehmer am Ende eine sehr interessante Frage:

Welches Feature fehlt Dir in C# noch?

Eine interessante Frage wie ich finde, über die ich mir bisher noch nicht so wirklich Gedanken gemacht habe. Aber eigentlich musste ich nicht lange überlegen, denn mit der neuen Sprachversion hat Microsoft fast alles erfüllt, was ich mir gewünscht habe, bis auf einen wichtigen Punkt: Das validieren von Methodenparametern!

Wenn wirklich robuster und qualitativ hochwertiger Code geschrieben werden soll, sollten zu Beginn jeder Methode die Eingangsparameter auf Validität geprüft werden, d.h. Ist er null? Ist der String leer? Ist der Wert in einem gültigen Zahlenbereich?

public Person(string firstname, string lastname, int age)
{
    if (String.IsNullOrWhiteSpace(firstname))
        throw new ArgumentException(nameof(firstname));

    if (String.IsNullOrWhiteSpace(lastname))
        throw new ArgumentException(nameof(lastname));

    if(age < 1 || age > 120)
        throw new ArgumentException(nameof(age));

    // Eigentliche Aufgabe...
}

Ich versuche in allen meinen Projekten peinlichst genau darauf zu achten, dass diese Regel auch eingehalten wird. Dadurch treten Fehler direkt auf und nicht erst einige Module später, bei denen mit dem invaliden Wert gearbeitet wird. Dieser Luxus wird jedoch sehr teuer erkauft, durch sehr sehr komplexen Code. Eine Metrik für einfach zu verstehenden Code ist die zyklomatische Komplexität, welche die Anzahl der Ausführungspfade durch eine Methode angibt. Betrachtet man eine Methode mit 4 Parametern und prüft jeden Parameter nur auf ungleich null, ergibt sich bereits eine Komplexität von 4, ohne das eine Zeile Produktivcode geschrieben wurde. Hat eine Methode einen höheren Wert als 8, so gilt diese als sehr schwierig zu verstehen. Dadurch ergeben sich zahlreiche Nachteile wie schwierige Wartbarkeit, schlechte Verständlichkeit usw.

Darüber hinaus stellt sich mir die Frage, nach Sinn und Zweck, so etwas zu machen. Im Anweisungsblock der Methode sollte die eigentliche Aufgabe der Methode durchgeführt werden und dazu zählt es nicht, ob die Eingangswerte valide sind oder nicht. Genau genommen ist es sogar eine Verletzung des Single-Responsibility-Principles. Was ist also die logische Konsequenz? Das Ganze prüfen, bevor die Methode ausgeführt wird:

public Person(string firstname, string lastname, int age)
	ensure !String.IsNullOrWhiteSpace(firstname) 
			else throw new ArgumentException(nameof(firstname))
	ensure !String.IsNullOrWhiteSpace(lastname) 
			else throw new ArgumentException(nameof(lastname))
	ensure !(age < 1 || age > 120)
			else throw new ArgumentException(nameof(age))
{
	// Eigentliche Aufgabe...
}

Dadurch wird der Code nicht unbedingt lesbarer, aber die Aktivität der Parameterprüfung hat einen fest definierten Ort und der Methodenbody bleibt für die tatsächliche Aufgabe reserviert.

Das Konzept mag etwas an CodeContracts erinnern, in der Tat geht es auch in diese Richtung. Allerdings benötige ich für CodeContracts mindesten das .NET Framework 4.0, da sie nicht Bestandteil der Sprache, sondern des Frameworks sind. Die reine Parameterüberprüfung könnte durch Compiler-Magic in ganz normalem IL-Code resultieren, der sogar abwärtskompatibel wäre. Darüber hinaus werden in C# die CodeContracts in der Methode definiert:

public Rational(int numerator, int denominator) 
{ 
    Contract.Requires( denominator ! = 0 );

    this .numerator = numerator;
    this .denominator = denominator;
}

Eine viel bessere Lösung kommt von Microsoft selbst, in der Forschungssprache Spec# (Die aus C# entstanden ist), wurden die CodeContracts ähnlich umgesetzt, wie ich es mir für C# wünsche. Die Sprache wurde jedoch nur für die Umsetzung des Betriebssystem Singularity erstellt, welches komplett in Managed Code geschrieben wurde:

int ISqrt(int x) 
	requires 0 <= x; 
	ensures result*result <= x && x < (result+1)*(result+1); 
{ 
	int r = 0; 
	while ((r+1)*(r+1) <= x) invariant r*r <= x; 
	{ 
		r++; 
	} 
	return r; 
}

Leider wurde Spec# zusammen mit Singularity eingestellt und die Erkenntnisse davon offensichtlich nicht in C# übernommen.

Auch wenn C#  mit der Version 6.0 fast perfekt wird, würde ich mir eine Kombination aus den CodeContracts aus Spec# und abwärtskompatibler Compiler-Magic für C# 7.0 wünschen!

Kommentare (4) -

  • Johannes

    16.01.2015 17:13:29 | Antwort

    Meines Wissens sind die Code Contracts doch in C# eingebaut und finden sich im System.Diagnostics-Namespace (msdn.microsoft.com/.../...tract%28v=vs.110%29.aspx). Mal davon abgesehen, dass die Asserts im Methodenkörper landen, entspricht das doch dem was du dir vorstellst oder?

    Schöne Grüße
    Johannes

  • David

    18.01.2015 23:29:26 | Antwort

    Hallo Johannes,

    Genau, auf die bin ich ja eingegangen (siehe Listing 3) Aber das ist ein .NET und kein Sprachfeature. Wenn ich das nutzen möchte, muss ich mindestens .NET 4.0 verwenden und hab es dann noch inline in der Methode, da gehört es meiner Meinung nach aber nicht hin. Cool wäre es, wie gezeigt, vor dem Body und wenn der Kompiler es dann in normalen inline Code umwandeln würde, wäre es auch abwärtskompatibel Smile

    Gruß David

  • David

    19.03.2015 22:41:01 | Antwort

    Hallo Stefan,

    nein, das kannte ich noch nicht. Aber sehr gut, scheint genau in diese Richtung zu gehen... Großartig!

Kommentar schreiben

Loading