Mittwoch, 25. Dezember 2013

C# Style Guide - Der Präfixfehler

In C# werden ja so einige Dinge aus anderen Programmiersprachen übernommen. Sehr häufig sind dies die Präfixe aus C++ oder auch aus VB. Genauer eigentlich die Präfix-Abkürzungen.
Dabei ist es in C# recht eindeutig definiert (msdn):
X DO NOT use Hungarian notation.
und hier
X DO NOT use a prefix for field names.
For example, do not use "g_" or "s_" to indicate static fields.

Hintergrund


Das Problem ist, dass Microsoft dies den Programmieren selber beigebracht hat Präfixe zu verwenden. Grund hierfür war damals der schlechte Editor. In VB 6 war der Editor damals am weitesten entwickelt und beherrschte Code Vervollständigung. In VC++ funktionierte dies nicht so recht. Da war der Borland Builder viel weiter voraus. Somit entschlossen sich viele Entwickler die VC Umgebung erst gar nicht zu nutzen, sondern einfach einen anderen Editor zu verwenden, der dann ein paar mehr Features zu bieten hatte.

Um nun nicht ständig Compiler Fehler zu bekommen, weil man sich mal wieder verschrieben hatte, wurden die Namen der Variablen stark verkürzt und zusätzlich Präfixe verwendet. Zu dieser Zeit entstand sicher auch die Verwendung des zwErgs (heute 'zwischenErgebnis') :-).


VB - Präfix-Abkürzungen für Klassen


Microsoft definierte in VB eine Menge Präfixe (siehe msdn). Ich kenne bis heute nicht alle auswendig. Ich weiß nur, dass selbst hier ein gewisses Chaos herrschte, da z.B. für die ComboBox 'cbo', aber auch 'cbx' im Umlauf war / ist.

In C# hat man dieses Problem beseitigt in dem man einfach sagt, es gibt keine Prefix-Abkürzungen mehr. Statt cbx schreibt man nun comboBox als Präfix. Hierfür ist keine Übersetzungstabelle mehr notwendig und man muss auch nicht sonderlich viel lernen.

Dies kann man an einem praktischem Beispiel erkennen. Man öffnet den Designer für ein Form und fügt dem Form eine ComboBox hinzu. Der Designer wird automatisch eine Variable anlegen, die mit 'comboBox' beginnt. Mal abgesehen von den MenuItems (da macht der Designer ein Postfix draus) zieht sich dies dann so durch.


VB - Prefix-Abkürzungen für einfache Datentypen


Sehr beliebt war es auch in die Variablennamen den Datentyp mit zu integrieren via "ungarische Notation" (siehe Wikipedia) . Hier wurde immer der Anfangsbuchstabe des Datentyps verwendet. Z.B. iZahl für eine Variable vom Typ Integer.

Das Problem bei dieser Art der Namensvergabe ist der, dass wenn sich der Typ ändert sich automatisch auch der Name ändern muss. Sonst macht das ja alles keinen Sinn.
Also beginnt beim ändern des Typs plötzlich ein großes Namen-Refactoring. Obwohl sich nur eine Zeile ändert hatte man dann plötzlich eine Datei mit 40 Änderungen, die man im Bedarfsfall alle anschauen musste, um herauszufinden was sich genau geändert hat.

Dazu kam, dass man ständig die Variablen suchte, da man zum einen den Datentyp wissen musste und zum anderen den Namen. Das hat öfter mal Zeit gekostet, die richtige Variable zu finden.
z.B. Was nehme ich wohl iZahl oder sZahl ?

In C# gibt es sowas nicht mehr. Der Name einer Variable drückt einzig und allein deren Verwendungszweck aus und beinhaltet nicht noch zusätzliche Metainformationen zu Typ, Zeilennummer oder Mondphase.

Wird der Variablentyp geändert, so kommt vom Compiler entweder ein Fehler oder nicht. Wenn ein Fehler auftaucht muss man bewusst Änderungen am Code vornehmen.

In der Präfix Variante mischen sich Compilerfehler und Namensänderungen, was das Nachverfolgen von Änderungen stark erschwert.

C++ 


In C++ war man damals sehr kreativ, wie man bestimmte Variablen kennzeichnen sollte, damit man sich in der Codevervollständigung 'einfach' zurechtfinden konnte und globale, lokale, statische Variablen trennen konnte.
Hier gibt es meist folgende Präfix-Definition.

's_' - statische Variable
'm_' oder '_'  - klassenglobale Variable

Das 'm' stammt aus der "Ungarischen Notation" (wikipedia) und wird hier Member genannt.
In VB gib es auch das m-Präfix (siehe msdn) und bezeichnet das Modul.

Dies macht es in C# recht schwer zu beurteilen, wie man nun dieses 'm' bezeichnen soll. Hier muss man zuerst erfragen, welcher Programmierer den Code geschrieben hat und von welcher Programmiersprache dieser gewechselt ist. Es kann ja theoretisch noch eine andere Sprache sein in der der m-Präfix eine ganz andere Bedeutung hat.


Der Nachteil von solchen Konventionen ist, dass jeder über diese Bescheid wissen muss. Das heißt, jeder muss die Konvention kennen, um sie nutzen und verstehen zu können. Es darf aber auch nur eine einzige Konvention geben. Diese darf sich also nicht nach 3 Jahren ändern.

Auch, wenn viele C++ Programmierer diese Definition für äußerst lebenswichtig halten und ohne diese gar nicht mehr arbeiten könnten, gibt es dies nicht in C#. Denn es geht auch ohne.

Klassenglobale Variablen werden mit "this." eingeleitet.
Statische Variablen werden mit dem Klassennamen kombiniert. [Klassenname].[statische Variable]

Die erste Reaktion, die ich hier erwarte, ist "Ohhh Gott. So viel zu tippen. Wie umständlich."

Aber ich kann da jeden beruhigen. So viel mehr tippen muss man da gar nicht. Um das Wort "this" gefolgt von einem Punkt einzutippen benötige ich weniger als eine Sekunde. Der Zeitaufwand sollte also im Rahmen bleiben.
Wer möchte kann aber auch, während er das Wort "this" eintippt, darüber nachdenken, was er nach dem Punkt eigentlich eingeben möchte.

Wo liegt aber der Vorteil in dem "this.". Ganz einfach Compilersicherheit.
Ich kann mir bei einem "this." hundertprozentig sicher sein, dass die globale Variable der Klasse verwendet wird. Bei einem "_" ist das nicht ganz so sicher. Nicht immer ist ein Entwickler so fleißig und benennt die Variablen um, wenn er eine globale Variable in eine lokale ändert.

Das Gleiche gilt für statische Variablen. Füge ich eine lokale Variable mit gleichem Namen aus versehen (oder mit Absicht) hinzu, wird trotzdem die statische Variable genommen, da ich ja den Klassennamen mit angegeben habe.
Erstelle ich aber eine lokale Variable unter Missachtung aller Konventions-Gesetze und benenne diese dann mit dem Prefix "s_", obwohl diese gar nicht statisch ist und ich nur keine Lust zum Umbenennen habe, habe ich im Code Chaos erzeugt. Macht man dies dann mehrmals kann man die Sache mit dem Verstehen total vergessen.

Somit kann man sagen, die Sache mit der Verwendung des Unterstrichs ist ok, wenn sich jeder ganz penibel dran hält. Sicherer ist es aber "this." zu verwenden, da mich dann der Compiler kontrolliert.

Auch wird man zum Refactoring gezwungen, wenn man die Variable im Kontext verschiebt. Dies bedeutet, dass man auf jede Stelle hingewiesen wird und diese nochmals kontrollieren muss.
Bei der "_"-Konvention muss man darauf hoffen, dass der Programmierer die Variable auch umbenennt. Er wird ja nicht gezwungen.



Fazit


Das "rüberschwappen" der Prefix-Konventionen aus anderen Programmiersprachen sollte beim Umstieg auf C# auf jeden Fall vermieden werden. Jede Sprache hat Konventionen. Somit auch C#. Und diese sollten beachtet werden. Keine "Ungarische Notation" in C# verwenden.

Die Verwendung von Präfixen war schon immer eine Fehlerquelle. Diese beruhte auf Definitionen, die strickt eingehalten werden mussten, um das Verständnis und die Funktion zu erhalten. Wurde von der Definition abgewichen kam es zu Chaos oder Fehlverhalten (Bugs), da diese Lösung nicht vom Compiler geprüft werden konnte.

Mit der Abkehr von diesen Regeln und der Verwendung von Compiler verständlichen Regeln kann es nicht mehr zu den genannten Problemen kommen. Dinge die Compiler verständlich als global definiert werden, werden auch immer auf global geprüft. Gleiches gilt für statische Variablen. Ein Aushebeln ist nicht mehr möglich.

Auch eine Kommunikation der speziellen Regeln ist nicht notwendig, da jeder Programmierer wissen sollte, dass sich die Variable "this" immer auf den globalen Klassenkontext bezieht, da dies sich bereits in der formalen Definition der Programmiersprache C# befindet (übrigens auch in C++).

Dazu kann man aber auch immer im Microsoft Style Guide nachlesen in der die C# Konvention definiert ist.