C#-Witze

Um das Jahr ausklingen zu lassen, erlaube ich mir mal einen etwas weniger ernsten Blogbeitrag: Ich habe mich gefragt, ob es bereits Programmiererwitze gibt über C# und bin via Google auf die Suche danach gegangen. Resultat: Ich bin über eine ganze Menge guten Humor betreffend Informatik und Programmieren gestolpert, aber ganz spezifisch zu C# als Programmiersprache habe ich herzlich wenig gefunden.

Hier ein Witz zu C#, den es wohl auch analog zu anderen kontroversen Themen gibt, gefunden in diesem Forum-Thread und von mir etwas umformuliert:

Ein C#-Programmierer wird schwer krank und hat nicht mehr lange zu leben. Er geht zu einem Java-Programmierer und sagt zu ihm: „Ich brauche dringend deine Hilfe. Ich möchte bitte Java lernen.“ Der Java-Programmierer erwidert erstaunt: „Du hast doch dein Leben lang aus Überzeugung nur C# verwendet! Warum willst du ausgerechnet jetzt Java lernen?“ Der C#-Programmierer erklärt: „Lieber stirbt einer von denen als einer von uns.“

Im stets interessanten Blog Coding Horror bin ich auf meiner Humor-Suche über diesen Eintrag gestolpert mit dem witzigen Titel Compiler, It Hurts When I Do This, leider ohne einen eigentlichen C#-Witz, dafür mit einer lustigen Diskussion über Sinn und Unsinn von Identifiers in C#-Programmen, die so heissen wie reservierte Identifiers.

Ein weiteres Fundstück: ein C# Trivia Quiz, mit einem Teil 1 und einem Teil 2.

Das hier ist irgendwie selbst ein Witz, scheint aber tatsächlich passiert zu sein: Girl composes virus in C#.

Eine allgemeine Sammlung zum Thema Programmierer-Humor, die mir recht gut gefallen hat, findet man hier. Mein persönlicher Favorit daraus:

Why do programmers always mix up Halloween and Christmas? Because Oct 31 equals Dec 25.

Hier geht es darum, wie die Vertreter verschiedener Berufsgruppen (darunter natürlich auch Programmierer) in Afrika auf die Elefantenjagd gehen. Den Link habe ich aus demselben Forum-Thread wie obigen C#-Witz. Dort findet sich auch eine Aufforderung, man möge sich ausdenken, wie denn ein C#-Programmierer Elefanten jagen würde. Das ist gar nicht so einfach; hier trotzdem meine hoffentlich einigermassen witzigen Versuche dazu:

Frage: Wie jagt ein C#-Programmierer Elefanten?
Antwort: Gar nicht. Microsoft erweitert das .NET-Framework so schnell, in der nächsten Version sind sicher auch Elefanten drin.

Frage: Wie jagt ein C#-Programmierer Elefanten?
Antwort: Er programmiert mit Hilfe von Generics, verschachtelten Klassen und Delegates ein dichtes Gestrüpp und wartet ruhig, bis sich ein Elefant darin verfängt.

Frage: Wie jagt ein C#-Programmierer Elefanten?
Antwort: Mit try … throw … catch natürlich.

Frage: Wie jagt ein C#-Programmierer Elefanten?
Antwort: Er blendet ihn per Reflection und nimmt ihn dann gefangen.

Frage: Wie jagt ein C#-Programmierer Elefanten?
Antwort: Mit C# natürlich. Die Programmiersprache ist so scharf, sie schneidet auch durch Elefantenhaut.

Frage: Wie jagt ein C#-Programmierer Elefanten?
Antwort: Gar nicht. Er klaut dem C++-Programmierer und dem Java-Programmierer je einen Elefanten.

Frage: Wie jagt ein C#-Programmierer Elefanten?
Antwort: Gar nicht. Er geht zum Mono-Projekt, dort gibt es Elefanten kostenlos.

Na denn, ein gutes neues Jahr allerseits!

Veröffentlicht in Keine Kategorie. Schlagwörter: . 2 Comments »

Die Performance von C#-Programmen

Die Programme der Megos sind heute im Prinzip sehr schnell, denn sie basieren auf C++-Code, arbeiten nicht exzessive mit dynamisch alloziertem Speicher und setzen direkt auf dem WIN32 API auf. Da stellt sich die Frage, wie es mit der Geschwindigkeit wohl aussehen wird, wenn wir dereinst diese Programme ablösen werden durch solche, die in C# geschrieben sind, wo schon der kleinste String ein volles Objekt ist und man in Form des .NET Frameworks eine ganze Menge Code zwischen sich und dem WIN32 API hat?

Um hier etwas Klarheit zu schaffen, habe ich mich heute erstmal schlau gemacht betreffend folgender vereinfachter Frage: Wenn man Code hat, der in etwa dasselbe tut, einmal in C++, und einmal in C#, ist dann der C#-Code per se langsamer, und wenn ja, um wieviel etwa?

Es hat sich herausgestellt, dass diese Frage nicht ganz einfach zu beantworten ist, denn Zeitmessungen haben so ihre Tücken. Ich habe z.B. in einem Forum Postings von jemandem gelesen, der versucht hat, die relative Geschwindigkeit dieser beiden Programmiersprachen (bzw. deren Implementationen auf Windows, wie sie heute sind) mit Hilfe kleiner Schleifen zu vergleichen, und zurückmeldete, die C++-Version sei 28000 Mal so schnell wie die C#-Version. Was war passiert? Der C++-Compiler war so schlau zu erkennen, dass eine zeitfressende Anweisung innerhalb der Schleife von der Schleife unabhängig war und hat sie als Optimierung aus der Schleife herausgenommen…

C#-Source-Code wird vom C#-Compiler übersetzt zu CIL (Common Intermediate Language), früher auch MSIL (Microsoft Intermediate Language) genannt, einem prozessorunabhängigen Bytecode, wie man z.B. in der Wikipedia hier nachlesen kann. Dieser wird dann üblicherweise bei der ersten Ausführung des Codes per JIT (Just in Time) Compiler quasi „fliegend“ in direkte Prozessoranweisungen übersetzt und so schliesslich ausgeführt. Ob diese Übersetzung flüchtig ist oder dauerhaft abgespeichert wird, so dass später bei einer erneuten Programmausführung der Übersetzungsschritt gespart werden kann, hängt von verschiedenen Faktoren ab.

Kann es hier unter Umständen Geschwindigkeitsprobleme geben? Interpretiert man Bytecode statt direkt Prozessoranweisungen auszuführen, ist das schliesslich zwangsweise um einige Faktoren langsamer. Hatte nicht Java, das ebenfalls auf einem Bytecode basiert, vor Jahren zum Teil arge Geschwindigkeitsprobleme, bevor die HotSpot-JVM mit einem JIT-Compiler eingeführt wurde? Bei meinen Abklärungen heute erlebte ich diesbezüglich eine kleine Überraschung: Die Microsoft CLR scheint gar keinen Interpreter für CIL zu beinhalten, es wird nie interpretiert, sondern vor der Ausführung immer alles JIT-compiliert! (Details hierzu z.B. hier.) Also kann es hier keinen prinzipiellen Geschwindigkeitsverlust geben.

Das heisst nicht, dass keine CIL-Interpreter existieren: Mono hat zumindest in der Anfangsphase mit einem Interpreter gearbeitet, bevor ein JIT-Compiler zur Verfügung stand, und es gibt Dinge wie Dot Net Anywhere, einem CIL-Interpreter für Kleinstcomputer. Aber zumindest auf Windows lauert kein solcher und wartet auf Gelegenheiten, Performance zu vernichten.

Geschwindigkeitsunterschiede können sich auch durch Laufzeitprüfungen ergeben: C# überwacht normalerweise jeden einzelnen Array-Zugriff auf einen gültigen Index, während C++ das normalerweise nicht tut. In einem kleinen, synthetischen Testprogramm mit einer Schleife um Array-Zugriffe herum können sich hier schon einmal signifikante Unterschiede ergeben, aber ehrlich, in welchem Programm aus dem richtigen Leben wird schon so ein grosser Teil der Rechenzeit verbracht, und wer wollte allen Ernstes heute noch auf Laufzeitprüfungen verzichten?

Interessanterweise gibt es auch in C# Ansätze für Optimierungen, wenn man denn einmal ein Programmstück hat, das von Array-Zugriffen absolut dominiert wird. Im bereits erwähnten Thread finden sich folgende informative Code-Schnippsel:

Code A:

int[] arry = new int[20000];
for (int index = 0; index < 20000; ++index) { total += arry[index]; } [/sourcecode] Code B: [sourcecode lang="csharp"] int[] arry = new int[20000]; for (int index = 0; index < arry.Length; ++index) { total += arry[index]; } [/sourcecode] Code C: [sourcecode lang="csharp"] int[] arry = new int[20000]; foreach (int element in arry) { total += element; } [/sourcecode] Code B sei 20% schneller als Code A, weil durch die Verwendung von arry.Length der Compiler mitbekommt, dass er auf Bereichsprüfungen verzichten darf (wozu er wohl im Beispiel A nicht schlau genug ist), und Code C sei sogar 50% schneller als Code A, weil es offenbar foreach schafft, einen internen Index schneller durchzuzählen als das ein Programm explizit kann, und weil natürlich ebenfalls Bereichsprüfungen wegfallen.

Solche Tipps und noch viele mehr findet man offenbar in einem schlauen Buch namens Effective C#. Microsofts C# Programming Guide hat ebenfalls Informationen über Optimierungen, zu finden hier, und generelle Informationen darüber, was in Managed Code wie teuer ist, hier. Und wer einmal ganz tief in die CLR und ihre Objekt-Verwaltung hineinschauen möchte, um Ansätze für Optimierungen zu finden, kann das mit Hilfe der Informationen hier tun.

Zusammenfassend kann ich sagen, nirgends Hinweise darauf gefunden zu haben, dass man sich schon alleine durch die Verwendung von C# als Sprache generelle Performance-Probleme einhandelt, weil C# eben „langsam“ ist. Schätzungen, die ich angetroffen habe, gehen davon aus, dass im allgemeinen C#-Code 80% bis 90% der Performance von C++-Code erreicht. Das dürfte zu verkraften sein!

Veröffentlicht in Keine Kategorie. Schlagwörter: , . 9 Comments »

Mehrfachvererbung

Manchmal wird man ziemlich unverhofft mit einem Problem konfrontiert, das man schon lange kannte, aber von dem man nicht ernsthaft erwartete, ihm jemals zu begegnen. So ging es mir mit der Mehrfachvererbung.

Ohne mich je genauer damit zu beschäftigen, war ich bisher der Meinung, es sei schon in Ordnung, dass viele Programmiersprachen Mehrfachvererbung nicht unterstützen, denn auf der einen Seite waren mir mögliche und vor allem sinnvolle Anwendungen nicht klar, aber auf der anderen Seite möglicher Ärger wie z.B. schwer verständliche Programme sehr wohl.

Umso mehr überrascht es mich, dass ich mir beim Design unseres neuen .NET-basierten Programmierwerkzeugs Triton jetzt genau diese Mehrfachvererbung wünsche, sie aber natürlich in C# nicht bekomme!

Worum geht es? Ich möchte die Funktionalität von Controls für Forms erweitern, notabene die von Standard-Controls wie TextBox, CheckBox, Button usw. Ich brauche z.B. Unterstützung für eine „schlaue“ Validierung von Eingabewerten, wo ich zwischen „validiert in Bezug auf den aktuellen Wert des Controls selbst“ und „validiert auch unter Berücksichtigung gegenseitiger Abhängigkeiten“ unterscheiden möchte.

So entsteht der Bedarf, eine neue Angabe ValidationState bei den Controls unterzubringen, am liebsten gleich zusammen mit einigen Methoden, welche das Validieren unterstützen.

Natürlich steht es mir frei, von allen schätzungsweise etwa 10 System.Windows.Forms.Control-Klassen, die ich brauche, neue Klassen abzuleiten und bei allen diesen eine Property ValidationState einzuführen, aber so erhalte ich natürlich 10 verschiedene Klassen mit 10 völlig unabhängigen und inkompatiblen Properties.

Ideal wäre natürlich, die Basisklasse Control selbst um eine Property ValidationState plus zugehörigen Code erweitern zu können, aber zumindest mit C# 3.0 wäre der einzige Weg hierzu eine Modifikation des Framework-Source-Codes und eine Neukompilation, was für mich natürlich nicht in Frage kommt. Auch eine abgeschwächte Variante, bei der ich Control unverändert lasse, aber eine eigene Klasse SmartValidationControl oder so quasi in den Baum der Vererbung „einfügen“ darf, hat dieselben nicht erfüllbaren technischen Voraussetzungen.

Und hier kommt jetzt die Mehrfachvererbung ins Spiel: 10 von diversen Controls abgeleitete Klassen wären in Ordnung, wenn sie ausser vom jeweiligen Control auch noch von einer zweiten Klasse SmartValidationControl ableiten dürften!

Was wäre mit 10 UserControl-Klassen mit einer gemeinsamen Basisklasse und mit dem jeweiligen eigentlichen Control als Kind? Dieser Ansatz ist natürlich möglich, artet aber aus in eine umfangreiche Fleissaufgabe, eine Menge Properties der Controls „durchzuschlaufen“, damit sie im Designer als Properties des Containers für Änderungen zur Verfügung stehen.

Schlussendlich bin ich beim folgendem Ansatz gelandet, den ich als das „geringste Übel“ ansehe: Ich leite 10 Klassen von 10 verschiedenen Controls ab, gebe aber allen diesen Klassen ein bestimmtes Interface, das ich dann eben 10 Mal implementieren muss.

Bei meinen Abklärungen über Mehrfachvererbung per Google bin ich über ein paar Seiten gestolpert, die sich vielleicht lohnen weiterzugeben:

Hier erläutert jemand aus dem C#-Team von Microsoft, dass die CLR selbst offenbar einen Weg bietet für Mehrfachvererbung, damit zumindest im Prinzip die Möglichkeit besteht, .NET-basierte Compiler zu bauen für Sprachen, welche Mehrfachvererbung umfassen.

hier hat jemand Mehrfachvererbung in C# so sehr vermisst, dass er sich eine „Simulation“ derselben ausgedacht hat, die recht trickreich mit Operatoren für implizite Typumwandlungen arbeitet. (Ich finde das Resultat insgesamt gesehen trotzdem ernüchternd.)

Und hier findet man schliesslich ein schon etwas älteres Interview mit dem „C# Compiler Program Manager“ bei Microsoft, dem man die offizielle Haltung von Microsoft zum Thema entnehmen kann.

Anhand der vielen Treffer, die Anfragen nach multiple inheritance oder auch spezifisch c# multiple inheritance in Google produzieren, kann man sehen, dass es sich offenbar um eine Sache handelt, die immer wieder aufkommt. Es wäre interessant zu wissen, ob es keine elegante Lösung des Problems geben kann, weil das Universum eben ist wie es ist, oder ob einfach noch niemand die geniale Idee gehabt hat…

Veröffentlicht in Keine Kategorie. Schlagwörter: , , . 4 Comments »

Compiler Bootstrapping

Beim Lesen eines Berichts über die PDC 2008 in Los Angeles stiess ich auf ein Schnippsel Information, das ich ganz interessant fand: Für die nächste Version C# 4.0 sei unter anderem geplant, den C#-Compiler (bisher in C++) in C# neu zu schreiben.

Ich habe an der ETH Zürich studiert, wo vor allem dank Niklaus Wirth eine ganze Reihe von Sprachen und zugehörigen Compilern entstanden sind (darunter Pascal, Modula-2 und Oberon), und habe den Eindruck bekommen, dass dabei stets versucht worden ist, so schnell wie möglich zu einem „self-hosting compiler“ zu kommen, d.h. zu einem Compiler, der in der Sprache geschrieben ist, die er compiliert.

Ich war deshalb überrascht zu lesen, dass in der Microsoft-Welt bis heute ein in C++ geschriebener Compiler allen C#-Code compiliert, und hatte sogar gewisse Zweifel, ob das wirklich stimmt – man soll ja nicht alles glauben, was im Internet steht…

Es ist nicht ganz einfach, eine klare Bestätigung zu finden für C++ als Implementations-Sprache des aktuellen C#-Compilers von Microsoft, aber dieser Thread hat mich schliesslich überzeugt.

Die Leute vom Mono-Projekt hatten es einfacher, weil sie den Microsoft-Compiler verwenden konnten, um ihren Compiler von Anfang an in C# zu schreiben, wie man z.B. hier nachlesen kann. Interessant ist, dass offenbar auch ein Microsoft-Mitarbeiter schon recht früh einen C#-Compiler in C# geschrieben hat (er berichtet über dieses Projekt hier), aber offenbar hat das die Compiler-„Hauptlinie“ nicht beeinflusst.

Was hat die Konkurrenz so abgeliefert in Sachen „Compiler-Bootstrapping“? Bei Java hat es nach dem allerersten, noch in C geschriebenen Compiler offenbar auch etwa 3 Jahre gedauert, bis ein in Java selbst geschriebener Compiler zur Verfügung stand, wenn man diesem Artikel glauben darf.

Wie man in der Geschichte des ersten Pascal-Compilers für die CDC 6000 nachlesen kann, wurden sogar schon Compiler von Hand übersetzt, um zu einem self-hosting compiler zu kommen. Das waren noch Zeiten…

Veröffentlicht in 1. Schlagwörter: , . 1 Comment »

Framework Studio

Als die Megos vor einiger Zeit vor der wichtigen Frage stand, womit sie in Zukunft ihre Software entwickeln will, haben wir uns ein Produkt der deutschen Firma Framework Systems näher angeschaut, das sich Framework Studio nennt.

Ich war überrascht, was ich da zu sehen bekam, denn dieses System hat eine sehr interessante Architektur. Es läuft für mich quasi unter dem Motto „Erstaunlich, was man mit .NET alles machen kann“.

Beim Framework Studio handelt es sich um ein Software-Entwicklungssystem. Es entstehen Web-Applikationen, die man mit Hilfe einer Java-basierten Client-Komponente und einer C#/.NET-basierten Server-Komponente ausführt, zwischen denen via SOAP kommuniziert wird. Applikationen entstehen einerseits durch stark Tool-unterstützes Spezifizieren von Forms, Datenbank-Strukturen und Verarbeitungs-Logik, und andererseits durch Programmieren spezieller Logik direkt in C#.

Wie ein ausführlicher Artikel (Teil 1, Teil 2) erläutert, erlauben die Architektur des Systems und die Struktur der damit geschaffenen Applikationen eine ziemlich weit gehende Anpassbarkeit von Standard-Software.

Die Firma Nissen & Velten Software GmbH hat offenbar eine ERP-Lösung namens NVinity mit Hilfe des Framework Studio realisiert.

Trotz aller Faszination über das, was Framework Systems hier geleistet hat, haben wir auf den Einsatz von Framework Studio verzichtet. Ein Hauptgrund war unsere Entscheidung, nicht oder zumindest nicht ausschliesslich auf Web-Applikationen zu setzen, wie das hier nötig gewesen wäre. Jemandem, der hierin kein Problem sieht, kann ich aber durchaus empfehlen, sich die kostenlose Probeversion des Produkts mal anzuschauen. Selbst wenn es am Schluss nicht zu einem tatsächlichen Einsatz reicht, kann man als Software-Architekt mindestens die eine oder andere Idee mitnehmen und in seine „konventionelle“ C#-Applikation einbauen.

Wenn dies nun etwas gar nach Werbung klingt: Nein, ist es nicht, aber ich kann eben meine Sympathie für eine Firma nicht verhehlen, die wie die Megos gegen Ende der 1980er-Jahre mit EMBASSY den Mut hat, Neues in einer Art zu versuchen, die man eher bei viel grösseren Firmen erwarten würde.

Etwas beunruhigend, dass offenbar NVinity immer noch die einzige vorzeigbare in Framework Studio realisierte Applikation ist. Da liegt noch viel Arbeit vor der Firma Framework Systems; Innovationen haben es eben oft schwer…

Veröffentlicht in 1. Schlagwörter: , . Leave a Comment »

Eine Art neues Grid für den Designer

Es ist im Designer des Visual Studio 2008 sehr viel einfacher geworden als früher etwa mit dem VB6-Designer, gutaussehende Forms zu produzieren, vor allem weil dank der Snaplines das Positionieren von Controls sehr viel einfacher geworden ist.

Dies ist auch gut so, wenn die Megos ihre Moneysoft-Applikationen, bisher entwickelt in einem einem eigenen System namens EMBASSY, in den nächsten Jahren in .NET neu schreiben will, denn wie man an folgendem, schon etwas älteren Screenshot sehen kann, wird es da oft Forms geben mit Dutzenden von Controls:

Valor Transaktionen

(Die vielen Controls in dieser Form sind nicht etwa schlechtes Design, sondern es gibt schlicht und einfach zu sogenannten Transaktionen in unserer Wertschriftenbuchhaltung Valor eine Menge Angaben, und die wollen alle erfasst werden. Für Kontrollen ist zudem ein schneller Überblick nötig, ohne dass man x Tabs wechseln muss.)

Als ich einmal eine solche Form testweise mit dem Visual-Studio-Designer nachbaute, merkte ich allerdings schnell, dass selbst die Unterstützung durch Snaplines noch nicht das ist, was ich eigentlich gerne hätte, um wirklich produktiv arbeiten zu können. Die Crux ist die: Forms, die praktisch nur aus vielen Labels, TextBoxes und GroupBoxes bestehen, sehen am besten aus (und die Controls haben am besten Platz), wenn man die Controls so positioniert und ihre Grössen so setzt, als würde man mit einem sehr speziellen Grid arbeiten – einem Grid, das mit einer „Zeilenhöhe“ und einer „Durchschnittszeichenbreite“ funktioniert.

Der Visual-Studio-Designer ist sehr modular aufgebaut und bietet eine Menge Möglichkeiten, Design-Prozesse zu beeinflussen, etwa mit Hilfe von ControlDesigner-Klassen, aber eine Möglichkeit, das Verhalten des Grids zu steuern oder zu übersteuern, scheint es nicht zu geben. Will man trotzdem mit einem speziellen Grid wie dem soeben beschriebenen arbeiten, muss man also zu einem Trick greifen.

Mein Trick ist der folgende: Controls haben eine protected virtual, also überdefinierbare Methode namens SetBoundsCore, die für das Setzen der Position und der Grösse eines Controls zuständig ist. Hier greife ich ein und runde Koordinaten auf mein Spezial-Grid, bevor ich sie an die Basis-Methode übergebe.

Das ist eine zwar drastische, aber einfache und effektive Methode, um x und y auf bestimmte Werte zu zwingen. Nichts und niemand im ganzen System kommt um die von mir etablierte Rundung der Koordinaten herum, auch der Designer nicht. Der Effekt im Designer ist der, dass man zwar ein solches Control nach wie vor mit der Maus frei herumziehen kann, aber sobald man es „absetzt“, kommt die geänderte SetBoundsCore-Methode zum Zug, und das Control hüpft sozusagen von sich aus auf die „richtige“ Position gemäss Grid. Das ist natürlich ungewohnt und am Anfang auch etwas gewöhnungsbedürftig, fühlte sich aber für mich schon nach kurzer Zeit sehr angenehm an.

Folgendes kleine Code-Fragment zeigt ein Label-Control, welches man nur gemäss einem Grid positionieren kann, dessen Zellen 30 Pixel breit und 15 Pixel hoch sind (vielleicht nicht unbedingt nützlich, zeigt aber den Effekt im Designer sehr schön):

  public class GriddedLabel : Label {
    protected override void SetBoundsCore(int x, int y, int width, int height, BoundsSpecified specified) {
      x = (x / 30) * 30;
      y = (y / 15) * 15;
      base.SetBoundsCore(x, y, width, height, specified);
    }
  }
Veröffentlicht in Keine Kategorie. Schlagwörter: , , , . Leave a Comment »

ReadOnlyCollection

Jeder, der eine halbwegs umfangreiche Klassen-Sammlung in C# designen muss, hat gute Chancen, auf folgende grundlegende Design-Frage zu stossen: Eine Klasse soll eine List enthalten, die zwar gegen aussen sichtbar ist, die man aber nicht direkt verändern darf – sämtliche Änderungen sollen quasi indirekt mit Hilfe von Methoden der Klasse erfolgen. Für eine solche Anforderung kann es viele denkbare Gründe geben; schon nur, wenn man die Anzahl Änderungen an der List zählen möchte, sollte es besser nicht möglich sein, dass sich jemand an der Zählung vorbeimogelt, indem er sich die Liste geben lässt und sie dann selbst verändert.

Ein kleines synthetisches Codebeispiel hierzu:

namespace ReadOnly {
  class Container {

    private List<int> numbers;
    public List<int> Numbers {
      get { return numbers; }
    }

    public void AddNumber(int n) {
      numbers.Add(n);
    }

    public Container() {
      numbers = new List<int>();
    }
  }

  class Program {
    static void Main(string[] args) {
      Container c=new Container();
      c.AddNumber(5);     // soll nur so funktionieren
      c.Numbers.Add(5);   // soll verboten sein
    }
  }
}

Mein erster Gedanke war, dass es in C# vielleicht ein Äquivalent zu C++ const gibt und man damit etwas machen kann. Eine Abklärung ergab, dass die CLR zwar eine gewisse Unterstützung bietet in dieser Richtung (für Managed C++), diese aber in C# nicht zur Verfügung steht.

Dann stiess ich auf die Property IsReadOnly, welche im Interface IList enthalten ist. Das sieht zwar auf den ersten Blick hoffnungsvoll aus, aber die Anwesenheit dieser Property im Interface heisst noch lange nicht, dass man im Falle einer „templated class“ etwas damit machen kann. Ich habe auf jeden Fall keinen Weg gefunden, im konkreten Falle einer List<int> deren Property IsReadOnly irgendwie zu überdefinieren, und selbst wenn dies gelänge, würden sich wohl Methoden wie Add der Template-basierten Klasse überhaupt nicht dafür interessieren.

Schliesslich förderte meine Suche die Klasse ReadOnlyCollection zu Tage, welche man wie folgt auf das Problem ansetzen kann:

namespace ReadOnly {
  class Container {

    private List<int> numbers;
    public ReadOnlyCollection<int> Numbers {
      get { return numbers.AsReadOnly(); }
    }

    public void AddNumber(int n) {
      numbers.Add(n);
    }

    public Container() {
      numbers = new List<int>();
    }
  }

  class Program {
    static void Main(string[] args) {
      Container c = new Container();
      c.AddNumber(5);     // funktioniert nur so
      c.Numbers.Add(5);   // compiliert nicht, 'ReadOnlyCollection.Add' gibt es nicht
    }
  }
}

Etwas unbefriedigend an dieser Lösung ist, dass mit Hilfe der Methode AsReadOnly ein neues Objekt erzeugt werden muss.

In irgendeinem Forum habe ich dann folgende Lösungs-Variante gefunden, welche ohne ein neues Objekt auskommt:

namespace ReadOnly {
  class Container {

    private List<int> numbers;
    public IEnumerable<int> Numbers {
      get { return numbers; }
    }

    public void AddNumber(int n) {
      numbers.Add(n);
    }

    public Container() {
      numbers = new List<int>();
    }
  }

  class Program {
    static void Main(string[] args) {
      Container c = new Container();
      c.AddNumber(5);     // funktioniert nur so
      c.Numbers.Add(5);   // compiliert nicht, 'IEnumerable.Add' gibt es nicht
    }
  }
}

Eine List<int> implementiert natürlich selbst bereits das Interface IEnumerable, so dass die Sache rein zur Compilierzeit stattfindet und die Property Number technisch gesehen gleich die Liste selbst zurückgibt. Unschön ist allerdings, dass man mit einem IEnumerable-Objekt nichts weiter machen kann als per foreach die Elemente durchgehen; noch nicht einmal die Anzahl Elemente kann man direkt abfragen.

Nach einigem Abwägen habe ich beschlossen, für die Megos-eigene Klassen-Bibliothek Triton, die gerade entsteht, das Problem zu ignorieren und keine technische Vorkehrungen zu treffen, um mit Hilfe einer der geschilderten Methoden Veränderungen an solchen Listen zu verhindern. Wir sind wenige, erfahrene, disziplinierte Programmierer, die keine Mühe haben mit dem Befolgen von Konventionen wie „Verändere keine Listen-Properties direkt“.

Natürlich sähe das anders aus, wenn man z.B. eine kommerziell vertriebene Klassen-Bibliothek für den Gebrauch durch viele andere Programmierer bauen müsste. Aber auch da gäbe es vielleicht die Möglichkeit, die direkten Veränderungen selbst nicht zu verunmöglichen, sie aber dennoch zu entdecken und mit einer Exception zu beantworten, indem man einerseits die Anzahl Aufrufe von AddNumber zählt und andererseits an „strategischen“ Orten im Code die Anzahl Elemente der Liste gegen diesen Zähler prüft.

Veröffentlicht in Keine Kategorie. Schlagwörter: , . Leave a Comment »