In meiner täglichen Arbeit mit C# (und der dazugehörigen Grafikkrankheit XAML und dem Bloatmonster Visual Studio ... kurz: .NET) werde ich immer wieder mit Dingen konfrontiert,
bei denen ich mich nur ungläubig fragen kann "Warum nur?".
Natürlich, das ist alles subjektiv. Ich leugne garnicht, dass es einfach eine kognitive Dissonanz zwischen den Konzepten dieser Programmiersprache und den Konzepten in meinem Denken gibt.
Aber, fragt euch eins:
Klingt Alan Kay fröhlich, als er über Entwicklung und Verbleib seines Gedankenkindes schreibt?
Hier also eine kleine Satireserie, gewachsen aus mehrjährigem Frust über die Diskrepanz zwischen Theorie und Realität.
Jetzt gackert die IDE aber rum, dass, wenn ich eine (übrigens vollkommen antiobjektorientiert öffentliche) Variable referenziere,
Ich möchte eine Liste definieren. Eine Liste ist, und das überrascht uns jetzt alle!, eine Auflistung von Dingen.
Ich erstelle eine grafische Oberfläche.
Wenn ich bei C# eine Variable erstelle, dann kann das nur innerhalb einer Klasse erfolgen.
Wiedermal heißt es, dass ich eine grafische Oberfläche erstellen soll.
Benutzeroberfläche, XAML, ihr wisst schon, was kommt.
Woher kann die Klasse, die ich hier nutze, eigentlich die Dinge, die ich nutze?
TeamFoundationServer.
XML Tabellen, 1326 Zeilen, erstellt mit einem Python Skript. Es funktioniert! Es macht Spaß! 13.03.2020
Benutzersteuerung. 16.03.2020
Kapselung soll die Handhabung von Daten vereinfachen.
Episode 1 - Alles ist ein Objekt.
doch bitte auch einen Objektverweis referenzieren soll. Wenn alles ein Objekt ist,
dann ist doch auch diese (übrigens vollkommen antiobjektorientiert öffentliche) Variable ein Objekt, oder?
Dann ist doch die Referenz auf die (übrigens vollkommen antiobjektorientiert öffentliche) Variable automatisch eine Referenz auf ein Objekt, oder?
WARUM ALSO ZUM SCHEISSDRECK MUSS ICH NOCH EIN EXPLIZITES OBJEKT REFERENZIEREN?
Meine Erklärungen für dieses Dilemma:
1. Nicht alles ist ein Objekt. Somit ist diese Aussage eine glatte Lüge und damit nutzlos.
2. C# ist nicht nach diesem Grundsatz gemacht worden, womit diese Aussage nicht auf C# anwendbar ist und damit nutzlos.
"Alles ist ein Objekt" ist also bestenfalls nutzlos.
Meine Lösungen für dieses Dilemma:
1. Hör auf, mir diese Lüge zu erzählen, dass "alles ein Objekt ist".
2. Hör auf, C# zu benutzen.
Meine Empfehlung: Beides.
Warum nur ...
Episode 2 - Typ(verun)sicher(t)heit
Gedacht, gemacht, ich definiere die Liste. Prompt beschwert sich die IDE, dass ich doch gefälligst einen Typ angeben soll.
Woher zum Henker soll ich wissen, was diese scheiß Liste für Typen enthalten wird?
Jeder Furz macht doch eh, was er will, und es ist meine Aufgabe als Programmierer (dafür werde ich bezahlt!),
dass mit dieser Liste am Ende sinnvolle Dinge passieren! Wenn mit dieser Liste am Ende keine sinnvollen Dinge passieren,
dann ist das nicht die Schuld der IDE oder der Programmiersprache. Also ist es auch nicht die Aufgabe der IDE oder der Programmiersprache,
mich daran zu hindern, sinnlose Dinge zu tun. Das nennt man Verantwortungsbewusstsein!
Wie auch immer, nach minutenlangem Rumprobieren hab ich endlich einen Typ gefunden, der mir die roten Linien erspart.
Ob es der Typ ist, den ich am Ende brauche, interessiert zu dem Zeitpunkt noch niemanden, weil das noch niemand weiß.
Und wer jetzt der Meinung ist, dass man das doch alles von Vornherein planen kann, der darf sich selbst ja gerne mal dabei beobachten,
wie oft er einen Plan an die Realität anpassen muss. Und wenn ich im Endeffekt sowieso alles über den Haufen schmeißen muss,
dann kann ich mir auch den Stress und die Zeit sparen, alles von vornherein festlegen zu müssen!
Vor Schlampigkeit schützt mich nicht die richtige Programmiersprache, sondern der richtige Programmierstil.
Warum nur ...
Episode 3 - Grafuck off!
Ich probiere diese grafische Oberfläche aus.
Ich stelle fest, dass Änderungen an der Auflösung des Fensters keine Auswirkungen auf die Fenstergröße haben.
Hä?
Ist das nicht dazu gemacht, gerade das zu tun? Im Vorschaufenster der IDE ändert es sich doch auch, warum also nicht im kompilierten Programm?
Ein Mysterium, das es zu ergründen gilt. Nach längerem Suchen stellt sich heraus,
dass in Visual Studio die Angaben für die Fenstergröße standardmäßig so eingestellt sind,
dass sie nur die Anzeige in der Vorschau beeinflussen, im fertigen Programm werden die Standardwerte genommen.
Klingt unlogisch, ist aber so.
Wie oft muss man einem gesunden Menschen auf den Kopf hauen, damit er auf so eine seltendämliche Idee kommt? Damit hat man erfolgreich einen
WYSINWYG-Editor geschaffen:
What You See Is Not What You Get
Warum nur ...
Episode 4 - Public equals Global
Dort wiederum muss ich festlegen, ob besagte Variable nun public oder private, manchmal auch protected sein soll.
C# wird immer als eine objektorientierte Programmiersprache beworben.
Gut, sie wird auch als "leicht zu lernen" beworben, wobei ich da in der Praxis aber auch diametral entegegengesetzte Erfahrungen gemacht habe.
Ein immer wiederkehrendes Konzept der OOP ist das Gehemnisprinzip, manchmal auch Kapselung genannt.
Dahinter steckt das Konzept, dass der Zustand eines Objektes nach außen hin verborgen sein soll.
Der Zustand eines Objektes? Achja, das sind die Daten, die es hält. Und Daten werden in Variablen gehalten.
Das beduetet aber, dass der Modifikator public, allein schon die Möglichkeit dazu, das komplette Gegenteil von Kapselung,
ganz zu schweigen von Geheimhaltung ist. Und wenn dann noch ein Setter ins Spiel kommt,
dann ist alles vorbei! Dann haben wir die perfekten Datenhuren, mit denen jeder machen kann, was er will.
Kann mir mal einer erklären, warum eine "objektorientierte" Sprache per Design die Grundzüge der Objektorientierung verletzt?
Warum nur ...
Episode 5 - What the graf ...
Ok, kann vorkommen, schließlich erstellen wir Anwendersoftware.
Ich versuche also, diverse Grids und StackBoxes und HassenichGesehens ineinander zu verschachteln,
da erscheint eine Fehlermeldung: "Das Content-Attribut wurde mehrfach vergeben. Der Designer ist deaktiviert."
Ah.
Ok.
Ähm.
Nein?
Klar hab ich das Attribut mehrfach vergeben, es ist schließlich notwendig für die Anzeige!
Logischerweise muss dann wieder der große Gockel herhalten, bzw sein wilder Cousin, die lahme Ente.
Nach mehrstündiger Suche stellt sich heraus: Das Windows-Element kann nur ein Tochter-Element beinhalten.
...
..
.
Ich bin also gezwungen, für zwei Zeilen Code vier (!) Zeilen Code extra einzufügen.
Warum nur ...
Episode 6 - Grafik, Grafik, überall Grafik!
Zwei Spalten, in der einen sind Radiobuttons angeordnet die korrespondierend dazu in der zweiten Spalte Eingabezeilen erhalten.
Über den Radiobuttons noch ein kleiner Schriftzug, damit man auch weiß, was gemeint ist. Somit sind das drei Zeilen insgesamt.
Setze ich nun in der zweiten Spalte die erste Eingabezeile im definierten Grid per Koordinaten in die zweite Zeile,
sollte sie eigentlich auf Höhe des ersten Radiobuttons sein. Eigentlich, also in der Theorie.
Aber in der Realität sitzt die Eingabezeile in der ERSTEN Zeile, also auf Höhe der Überschrift für die Radiobuttons.
Ok, füge ich eben ein leeres Label ein (Label = Content, TextBlock = Text; Konsistens? Wozu?!?) als Platzhalter.
Nochmal kontrolliert, Grid ist alles richtig definiert, Koordinaten sind richtig vergeben, kompilieren.
Die erste Eingabezeile ruscht jetzt auf die dritte Zeile, somit auf Höhe des zweiten Radiobuttons, mit dem sie absolut nichts zu tun hat.
Ich habe mein Ziel perfekt verfehlt!
Warum nur ...
Episode 7 - Vererbung oder Inzest?
Ich drücke auf F12.
Erste Ebene der Vererbung, keine Dinge in Sicht. Ich drücke auf F12.
Keine Dinge in Sicht. Ich drücke auf F12.
Keine Dinge in Sicht. Ich drücke auf F12
[...] ein bisschen kommt es mir wie Need for Speed Most Wanted vor:
Ich starte neu, ich verliere, ich starte neu, ich verliere, ich starte neu, ich verliere ...
Und genauso wie bei Baron kann ich ein dutzend Mal auf F12 drücken, dann hat die Vererbungskette ein Ende.
Und der Suchende unterdessen hat das Thema für die Suche ganz einfach vergessen.
Ich will nur diese ver... Banane, nicht den ganzen ver... Dschungel!
Warum nur ...
Episode 8 - That F****** S****
Soll die Arbeit für Teams erleichtern, indem man zentral die Projekte verwalten kann und sich jeder den neuesten Stand einfach runterladen
und seine Änderungen einfach hochladen kann. Damit kann man also zentral sicherstellen, dass alle auf dem selben Stand sind. In der Theorie.
Neulich (also einmal die Woche) im TFS ...
"Hab alles eingecheckt, kannst die neuste Version abrufen."
"Ok, mach ich." Computer rödelt ein paar Minuten. "Compiliert nicht, Fehlermeldung."
"Was? Warum das denn, bei mir funktioniert es doch auch!"
"Ja, er hat wohl ein paar Verweise zerhackt."
"Ah, das wieder. Ja, dann müssen wir die per Hand nachfeilen."
Nach einer halben Stunde ist die Synchronisierung des Standes für gewöhnlich abgeschlossen.
Ein USB-Stick wäre schneller gewesen.
Immer wieder.
Warum nur ...
Episode 9 - I don't give a C#it!
Dasselbe Skript, nachgebaut mit C#. Spoiler: Ich habe festgestellt, dass diese Sprachen unterschiedlich sind. Wow! Erkenntnis!
C# ist wesentlich aufgeblasener, umständlicher und robuster als Python. Alles dreis sind schlechte Eigenschaften:
Aufgeblasener: 10 Zeilen mehr für weniger Funktionalität.
Umständlicher: Statische Typisierung. Muss ich mehr sagen?
Robuster: Warum ist das schlecht?
Ganz einfach: wenn durch Null dividiert wird, dann fliegt mir Python um die Ohren und sagt
"Lieber Programmierer, du hast Mist gebaut!" Dann kann ich eine Fehlerbehandlung einbauen und die Sache funzt.
C# schreibt heimlich, still und leise NaN in die Tabelle.
Ob die am Ende noch ausgewertet werden kann?
Probiers aus! Dann fliegt dir die ganze Chose eben fünf Schritte später um die Ohren.
Ob du dann noch rausfindest, wo der Fehler liegt?
Probiers aus!
Warum nur ...
Episode 10 - Just because they look equal ...
Filtere die Benutzer, die eine bestimmte Eigenschaft haben in eine gesonderte Liste.
Übergeben werden die Benutzer mit einem Manager, der von der Typdeklaration her identisch wie eine Liste aussieht. Na, das bekomm ich hin:
foreach(var Ding in Manager)
...
Fehlermeldung.
Warum geht das jetzt nicht?
Das Ding, das wie eine Liste aussieht, ist keine Liste. Es hat keine öffentliche Instanzdefinition für GetEnumerator, also kann ich auch nicht einfach so ohne weiteres darüber iterieren.
Offenbar sind bei C# Dinge, die identisch aussehen, vollkommen verschieden voneinander.
Und das ist ungefähr das, was man sich unter Polymorphie vorstellen kann:
Sieht genauso aus, ist was vollkommen anderes!
Warum nur ...
Episode 11 - Das .Netz
Dazu werden Daten in der Objektorientierung in Klassen versteckt.
Diese Klassen stellen dann Methoden zur Verfügung, mit denen dann auf diese Daten zugegriffen werden kann.
Das ist die Theorie.
In der Realität habe ich dann in einer Klasse beliebig viele sogenannte Felder (ein selbstgefälliger Begriff für Daten) mit jeweils unterschiedlich vielen, aber trotzdem vielen Verweisen.
Das bedeutet, dass andere Teile des Programmes auf diese Felder zugreifen. Jetzt stellt sich mir die Frage, wo da die Kapselung ist?
Die Daten werden zwar in Klassen geschrieben, aber trotzdem einzeln abgerufen.
Somit ist der Blödsinn mit den Klassen nur ein vollkommen unnötiger Schritt, der die Daten hinter sinnlosen Türen versteckt, aber keine echte Trennung bewirkt.
Das einzige, was passiert, ist ein dichtes Verflechten der Abhängigkeiten, die ein Bearbeiten extrem erschweren.
Man verstrickt sich in einem .Netz.
Warum nur ...