Das Megos .NET-Weblog

27. Juni 2009

Der Historical Debugger in VS2010

Gespeichert unter: Keine Kategorie — Schlagworte: , — René Brunner @ 13:21

Schon vor einiger Zeit hat Microsoft angekündigt, dass Visual Studio 2010 einen neuen, speziellen Debugger namens Historical Debugger enthalten wird. Man findet z.B. hier eine Ankündigung aus dem Oktober 2008:

Are you tired of constantly setting breakpoints to hone in on a pesky bug? How would you like to be able to step “back in time” through your debugger? The Historical Debugger in Visual Studio Team System 2010 promises to revolutionize your debugging experience.

Ich sagte mir, na das ist doch mal was Interessantes: Irgendwie rückwärts durch ein Programm debuggen? Weil ich für Revolutionen immer zu haben bin und als “Systemprogrammierer” einfach wissen muss, wie man einen Debugger implementiert, der so etwas leistet, bin ich der Sache heute auf den Grund gegangen und kann nun berichten, worum es geht.

Zunächst musste ich eine Hürde aus dem Weg räumen: Der Historical Debugger ist tatsächlich nur im Visual Studio Team System 2010 verfügbar, das “normale” Visual Studio ohne TS, das ich mir zuerst testweise installiert hatte, enthält ihn nicht. Man findet die VS2010-TS-Preview als ISO-Download hier.

Der Witz an der Sache ist der folgende: Der Historical Debugger schreibt selbständig zu bestimmten Zeitpunkten während der Programmausführung ein Log mit Informationen über den Zustand des Programms zum jeweiligen Zeitpunkt. Der angepriesene “Schritt zurück in der Zeit” besteht darin, nach einem Stopp des Programms aus der Debug History, d.h. der Liste der Zeitpunkte, einen Eintrag auszuwählen und sich im Debugger den Zustand des Programms anzuschauen, wie er damals war.

Das kann natürlich – im Prinzip – ganz nützlich sein: Hat man z.B. einen Breakpoint erreicht und stellt fest, dass eine Variable einen falschen Wert aufweist, muss man nun nicht mehr mit dem Debugging von vorne beginnen und sich vorsichtig an den Zeitpunkt heranpirschen, wo der falsche Wert zum ersten Mal auftaucht, sondern kann bequem und entspannt die Debug History verwenden, um den Zeitpunkt zu finden oder mindestens stark einzugrenzen, wo die Sache aus dem Ruder läuft.

Man kann in 2 Stufen wählen, wie die Zeitpunkte bestimmt werden sollen: Log schreiben entweder bei “wichtigen Systemereignissen” oder bei solchen Ereignissen plus bei jedem Methoden-Aufruf. Man kann hier erahnen, dass die Sache Potential hat auszuufern, denn Methoden-Aufrufe können ja sehr zahlreich sein.

Ins Log geschrieben werden der Call Stack zum Zeitpunkt und Variablen-Werte zum Zeitpunkt, wobei man bei Objekten nur noch das Resultat von ToString sieht, aber keinen Zugriff mehr bekommt auf die einzelnen Komponenten.

Ein paar Screenshots hierzu findet man im Blog Dariusz quatscht hier.

Meiner Meinung nach ist das ganz hübsch, aber ob es für eine Revolution reicht, ist fraglich. Insbesondere die doch sehr eingeschränkte historische Sicht auf Variablen-Werte enttäuscht. Mir ist schon klar, dass es schlicht unmöglich ist, zu Hunderten von Zeitpunkten den gesamten Heap ins Log zu schreiben, damit ich auch nachträglich noch Objekte mit all ihren Details anschauen kann, aber hey, es hätte ja sein können, dass jemand mit Hilfe einer genialen Idee ein Log implementiert hat, das viele Details mit praktikablem Aufwand speichern kann, z.B. mit einer Heap-Differenzen-Methode…

Was haben andere so gemacht in Sachen Debugger mit Historie? Hier ist von einem experimentellen Java-Debugger die Rede, der vor ein paar Jahren implementiert wurde und der offenbar alle Variablen-Zuweisungen während eines Programmlaufs protokolliert, indem er sich in die Pseudocode-Ausführung der JVM einklinkt. Er nennt sich denn auch treffend Omniscient Debugger (”allwissender Debugger”) und erlaubt offenbar die volle Sicht zu einem beliebigen Zeitpunkt.

In einer PDF-Datei, welche den Debugger genauer beschreibt, findet sich die Information, dass Apache Ant etwa 24 Millionen Variablen-Zuweisungen macht, während das Tool seine eigene Kompilation steuert. Das sind eine ganze Menge, aber nicht so viele, dass sie den Ansatz an sich gleich zu Fall bringen.

VMWare hat etwas im Angebot, das die Firma Replay Debugging nennt, beschrieben auf der Website hier. Unterstützt wird Debugging für C/C++, wie man im Artikel hier nachlesen kann. Das ganze funktioniert mit Hilfe der Virtual Machine, wo man natürlich wie bei der JVM eine zentrale Stelle hat, wo man sich einklinken kann, um zu protokollieren, was läuft. Das Debugging, sprich der Blick auf das Log findet statt in einem Visual Studio, das um ein VMWare-Plugin ergänzt wurde.

Anhand der Beschreibung vermute ich, dass komplett protokolliert wird und man so in einzelnen Schritten vorwärts und sogar rückwärts durch das Programm gehen kann. Die Suche nach dem Punkt im Programm, wo eine Datenstruktur “zerstört” wird, finde man so: Zeitpunkt finden, wo sie falsch ist, dann Programm “rückwärts laufen lassen” mit Daten-Breakpoint auf der Struktur und einfach auf den Break durch Veränderung warten.

Hier meldet sich natürlich wieder meine Neugier, die mich veranlasst hat, überhaupt erst den Historical Debugger von Microsoft anzuschauen und die sich nun fragt: Wie macht VMWare das? Ich vermute, dass die komplette Abfolge aller CPU-Befehle, welche das Programm ausführte, der zentrale Bestandteil des Logs ist, und dass das “Debugging” darin besteht, mit Hilfe der Virtual Machine das Programm irgendwie entlang dieser Abfolge zu führen, vielleicht gestützt auf Komplett-Speicherabzüge in gewissen Zeitabständen.

Und ich vermute ebenfalls, dass die Sache derart auf die Ausführung direkter CPU-Anweisungen zentriert ist, dass die Ausdehnung des Prinzips auf C# mit seinem JIT-Compiler eine schwierige bis unmögliche Sache werden dürfte.

6. Juni 2009

Die Performance von C#-Programmen

Gespeichert unter: Keine Kategorie — Schlagworte: , — René Brunner @ 15:05

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];
}

Code B:


int[] arry = new int[20000];
for (int index = 0; index < arry.Length; ++index) {
   total += arry[index];
}

Code C:


int[] arry = new int[20000];
foreach (int element in arry) {
  total += element;
}

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!

2. Mai 2009

Ein etwas nostalgischer Blick zurück

Gespeichert unter: Keine Kategorie — Schlagworte: , , — René Brunner @ 13:50

Als ich vor einiger Zeit ernsthaft mit Visual Studio und .NET/C# zu arbeiten begann, verglich ich diese Programmierumgebung ganz automatisch mit dem vor über 10 Jahren erschienenen VB6, mit dem ich vor allem durch eine privat damit gebaute Shareware sehr gut vertraut war.

Bei diesen beiden Systemen sind nur wenige Dinge exakt gleich, schon alleine wegen C# anstelle von BASIC als Sprache. Aber trotzdem hatte ich immer das Gefühl einer grossen Vertrautheit, den Eindruck einer weitgehenden Übereinstimmung bezüglich der grundlegenden Philosophie: Überall sah ich quasi den “Geist” von VB6 durchschimmern.

Was mich aber noch viel mehr beeindruckte, und auch immer stärker, je genauer ich Visual Studio und .NET kennenlernte, war die folgende Geschichte: Im Gegensatz zum Nachfolger ist VB6 geradezu ein Winzling, weist aber trotz seiner bescheidenen Grösse (und trotz seines Alters) eine erstaunliche Menge der wichtigsten technologischen “Errungenschaften” bereits auf.

Dinge wie Garbage Collection, IntelliSense im Editor, grafischer Form-Designer, hervorragendes Laufzeit-Debugging inklusive Watches und Immediate-Fenster, Code-Änderungen während das Programm läuft, mächtige String-Klasse, Kompilieren zu Native Code, Klassen inklusive Late Binding beim Aufruf von Objekt-Methoden, alles schon drin in diesem System von 1998, in einem Laufzeitsystem von nicht mal 3 MB und einer IDE von absolut lächerlichen 15 MB.

Natürlich könnte ich problemlos eine noch längere Liste von Eigenschaften aufzählen, die VB6 nicht hatte, ohne die man aber im Jahre 2009 zum Entwickeln von Software gar nicht anzutreten braucht.

Es geht mir um etwas Anderes: Der Vergleich VB6 versus Visual Studio 2010, C# und .NET 3.5 ist für mich mittlerweile eines der besten Beispiele, die ich persönlich kenne, um die 80%/20%-Regel zu illustrieren: Ja, das moderne System kann mehr als das gute alte VB6, aber man sehe sich mal an, wieviele der wirklich wichtigen, zentralen Eigenschaften VB6 bereits aufweist (die “80%” in besagter Regel), erreicht mit vergleichsweise bescheidenem Aufwand (den “20%”). Oder umgekehrt: Ja, das moderne System ist überlegen, aber man sehe sich mal an, mit welch gigantischen Aufwand gewisse Verbesserungen erkauft werden mussten.

Natürlich kann ich als reiner Anwender von Visual Studio und .NET das Resultat geniessen, denn Microsoft trieb ja quasi für mich den grossen Aufwand, nicht ich. Und einen modernen Rechner bringen auch grosse IDEs und Laufzeitsysteme nicht ins Schwitzen.

Aber wenn ich selber etwas baue, tue ich gut daran, immer mal wieder an die 80%/20%-Regel zu denken und diese zu beherzigen. Mir persönlich fällt das nicht schwer, mit einem etwas nostalgischen Blick zurück zu VB6 dann und wann, zu dem ich z.B. Gelegenheit habe, wenn mein PC die Visual-Studio-IDE mit viel Festplatten-Aktivität in den Speicher wuchtet und diese riesige Maschine anwirft…

18. April 2009

Und wieder mal TechDays

Gespeichert unter: Keine Kategorie — Schlagworte: , , — René Brunner @ 09:48

Etwas über ein Jahr ist es her, seit ich mit einem Eintrag über die Microsoft TechDays 2008 diesen Blog eröffnen durfte. Letzte Woche war ich nun an den TechDays 2009, dieses Mal im Kursaal in Bern.

Es war eine gut besuchte, professionell durchgeführte und solide Veranstaltung. Wie schon letztes Jahr waren es für mich gut investierte 2 Tage, um zu erspüren, wo die Reise hingeht mit Microsoft und den Technologien dieser Firma.

Die für die Megos vielleicht wichtigste Information, die ich mitgenommen habe: WPF ist jetzt einsatzfähig. Dies wurde klar anhand der Preisverleihung für den .NET Swiss Innovation Award, wo eine WPF- und 2 Silverlight-Applikationen prämiert worden sind – alle 3 nicht-triviale, real existierende Applikationen, offenbar reif für den produktiven Einsatz. (Ein paar Details zum Award und den Gewinnern findet man in einem Infoweek-Artikle hier.) Auch der Stand von WPF plus entsprechender Tools ist mittlerweile um Längen besser als noch vor zwei Jahren oder so, wo Microsoft vorschlug, WPF-Applikationen ohne ein DataGrid-Control und ohne IntelliSense für XAML zu entwickeln…

Eine weitere interessante, in einem gut gemachten Vortrag präsentierte Sache sind die Parallel Extensions, mit deren Hilfe man für seine Programme unter .NET 4.0 auf relativ einfache Weise mehrere Cores bzw. Prozessoren wird nutzen können. Zunächst schien mir die Sache etwas überspitzt formuliert ein wenig paradox: Da baut man in der Form des .NET-Frameworks zuerst erst ein System, das mit seinem Pseudocode-basierten Ansatz, Garbage Collecting, aufwendigen Laufzeit-Informationen für Reflection usw. ziemlich gut geeignet ist, um Prozessorleistung abzufackeln, und dann geht man hin und baut eine Erweiterung, um das sogar noch mit mehr als einem Core gleichzeitig zu tun.

Aber es macht schon einen gewissen Sinn: Dank JIT-Compilierung und mächtigen Prozessoren spielt der .NET-Overhead mit der Zeit eine immer kleinere Rolle, aber wie man die laufend grösser werdende Zahl an Cores einfach und sinnvoll nutzen kann, wird wohl eine der grossen Fragen der Zukunft sein, und zwar eine, die sich als ziemlich harte Nuss erweisen dürfte.

Zudem sieht man, dass es auch klare Vorteile hat, sich mit dem .NET Framework auf einer relativ hohen Abstraktions-Ebene bewegen zu können: Microsoft hat es in der Hand, seine .NET-Sprachen geeignet um neue Sprachkonstrukte für paralleles Programmieren zu erweitern, und das .NET Framework ist ein geeignetes System, um möglichst viele der “schmutzigen” Details vom Entwickler fernzuhalten und gewissermassen “hinter den Kulissen zu verstecken”.

Mehr zu diesem Thema find man auf der MSDN-Website hier.

Wie letztes Jahr besuchte ich mit grossem Genuss zwei Vorträge von Sascha P. Corti. Microsoft Schweiz soll gut Acht geben auf diesen Mann, den sollten sie unbedingt behalten.

Der für mich ganz persönlich absurdeste Moment dieser TechDays war, als stolz die Out of Browser genannte neue Fähigkeit der kommenden Version 3 von Silverlight präsentiert wurde: Damit ist es möglich, entsprechend vorbereitete Silverlight-3-Applikationen lokal zu installieren und dann komplett ausserhalb des Browsers laufen zu lassen. (Technische Details findet man z.B. hier).

Ohne Zweifel technisch gesehen eine beeindruckende Leistung, an der die Jungs und Mädels in Redmond wohl hart gearbeitet haben, um sie zu vollbringen. Aber warum fand ich das in gewisser Weise absurd? Lassen Sie es mich mit den Worten eines hypothetischen, fanatischen Web-2.0-Fans einmal so formulieren: “Wow! Ich dachte immer, man braucht einen Browser, um Applikationen laufen zu lassen. Jetzt geht das ja sogar ohne Browser!”. Ja, ja, wer hätte das gedacht, Applikationen, die lokal auf einem PC laufen.

10. März 2009

Die Geschichte vom Backslash

Gespeichert unter: Keine Kategorie — Schlagworte: — René Brunner @ 11:44

Ich arbeite nun schon eine ganze Weile mit der Powershell in der 2.0 CTP Version. Ich bin ganz zufrieden damit und kann eigentlich jedem nur empfehlen, sie einmal auszuprobieren.

Lustig ist, was mir mittlerweile am besten daran gefällt und worauf ich nicht mehr verzichten möchte: Die Powershell erlaubt es mir, in Filenamen den normalen Slash “/” anstelle des Backslash “\” zu verwenden.

Hinter dem Backslash als Trennzeichen in Dateinamen in Windows steckt eine ganz interessante Geschichte, die ich hier erwähnen möchte für Leute, die sie noch nicht kennen, auch wenn sie wenig mit .NET zu tun hat:

DOS 1.0 war eigentlich ein von Microsoft eingekauftes Betriebssystem namens QDOS, das in vielen Aspekten grosse Ähnlichkeit hatte mit CP/M. CP/M war ein Betriebssystem, das nur Laufwerksbuchstaben und Dateinamen, aber keine Ordner bzw. Subdirectories kannte. Für die Schalter bzw. Switches seiner Tools verwendete es den normalen Slash “/” als Einleitungszeichen – eine Konvention, welche DOS 1.0 übernahm.

Als dann DOS 2.0 eine anständige hierarchische Dateiverwaltung bekommen sollte, war die von UNIX her naheliegende Verwendung von “/” für die Dateinamen durch eben diese Verwendung für Schalter gewissermassen blockiert, worauf Microsoft auf das Nächstbeste d.h. auf den Backslash auswich.

Rückblickend kann man wohl sagen, dass Microsoft damals in den sauren Apfel hätte beissen und trotzdem den Slash verwenden sollen, auch wenn dies bedeutet hätte, eine Menge eigener und fremder Kommandozeilen-basierter Programme anpassen zu müssen.

Es gibt bei der Geschichte vom Backslash jede Menge Details, wie etwa das folgende: Die Microsoft-Programmierer selbst waren durch die Verwendung von XENIX, der damaligen Microsoft’schen UNIX-Variante, bereits an den Slash gewohnt und fanden den Backslash gar nicht lustig. Sie programmierten deshalb eine “geheime” Option namens SWITCHAR für die CONFIG.SYS, mit der man das Zeichen für Schalter auf ein anderes Zeichen als “/” umdefinieren konnte, wonach der normale Slash in Dateinamen zulässig wurde.

(Ich bin schon so lange im Geschäft, dass ich diese Option selbst noch verwendet habe, auf einem WANG PC; ich frage mich jetzt nur, wie ich eigentlich davon erfahren hatte damals…)

Eine ausführliche Version der Geschichte findet man hier, die Geschichte des Zeichens “\” selbst hier.

Windows akzeptiert übrigens in Filenamen bis heute sowohl Backslash wie auch Slash, auch wenn es bei Aufbereitungen und Abfragen von Dateinamen konsequent auf Backslash wandelt. Folgende .NET/C#-Zeile ist vollkommen ok und funktioniert:


      FileInfo f = new FileInfo("C:/Work/Backslash/Program.cs");

31. Januar 2009

IntelliSense und Erweiterungen von Klassen

Gespeichert unter: Keine Kategorie — Schlagworte: , — René Brunner @ 14:44

IntelliSense in Visual Studio ist eine wunderbare Sache, die das Programmieren ungemein erleichtert. Eigentlich sollte heutzutage niemand mehr in einer Umgebung programmieren müssen, wo kein in etwa gleichwertiges Feature zur Verfügung steht. Trotzdem sahen wir ein ernstzunehmendes Problem damit, als wir hier in der Megos ernsthaft damit begannen, unsere neues Programmierwerkzeug Triton in C# zu implementieren.

IntelliSense scheint seine Listen immer stur alphabetisch zu sortieren und immer alles aufzulisten, ohne Filtermöglichkeiten. Das kann dan zu einem Problem werden, wenn man Klassen erweitert, die bereits eine Menge Members haben, und dann in der IntelliSense-Liste seine “eigenen” Members (d.h. diejenigen der Erweiterung) kaum mehr findet. Je nach Art der Klassen benötigt man beim Implementieren bevorzugt eben diese eigenen Members, was das Ganze dann etwas mühsam macht.

Unsere Überlegungen zeigten uns, dass sich die Sache zuspitzt, wenn man Klassen für den Datenbank-Zugriff generiert, wo für jede Kolonne in einer Tabelle eine Property definiert wird, die sich dann zu all den Members der Zugriffs-Basisklasse gesellt und unter diesen irgendwie unterzugehen droht, obwohl sie doch zum eigentlichen “Witz” der generierten Klasse gehört.

Man kann das bereits bei Visual-Studio-eigenen typed datasets beobachten: Eine Tabellen-Zugriffs-Klasse wird von System.Data.TypedTableBase abgeleitet, einer Klasse mit geschätzten etwa 100 Members. Eine Kolonne TitelKurs wird zu einer Property namens TitelKursColumn und wird von IntelliSense unter T eingeordnet; eine Kolonne WährungsKurs wird zu WährungsKursColumn und taucht dann in der Liste unter W und damit an einem ganz anderen Ort auf. Dieses Verhalten verunmöglicht es praktisch, sich per IntelliSense einen schnellen Überblick über alle Kolonnen zu verschaffen – aber wo ausser da soll denn dieser Überblick herkommen?

Es war schnell klar, dass es bei Visual Studio und IntelliSense, so wie jetzt implementiert, nur ein einziges “Rädchen” gibt, an dem man überhaupt drehen kann, wenn man dieses Problem irgendwie lösen will: Will man Members zusammen sehen in einer IntelliSense-Liste, so müssen sie eben entsprechend heissen, d.h. alle mit irgendeinem Präfix beginnen.

Sie werden als geneigter Leser vielleicht schon ahnen, dass dieser Blog-Eintrag hier geradewegs auf ein Dilemma zumarschiert: Von Namenspräfixen wird in .NET und C# eigentlich abgeraten; verwendet man sie trotzdem, stecken einen gewisse Leute schnell einmal in die Schublade “Ewiggestrige”, die reserviert ist für Leute, die von ihren alten Gewohnheiten nicht lassen können und auch nicht verstanden haben, dass man heutzutage nur noch “natürliche” Namen vergibt, weil man – eben gerade durch Features moderner IDEs wie z.B. IntelliSense – nicht mehr darauf angewiesen ist, vom Namen eines Dings irgendwelche Meta-Information ablesen zu können.

Aber wie gesagt, hier geht es nicht darum, sondern um eine Lösung für ein kleines, aber sehr lästiges Problem mit IntelliSense.

Wir sahen das Ganze als Wahl zwischen zwei Übeln: 1) einen Teil der Namen mit Hilfe von Präfixen verunstalten, aber damit den Nutzen von IntelliSense aufwerten, oder 2) den Empfehlungen und der Mehrheit der C#-Programmierer folgen, aber sich mit Situationen abfinden, in denen IntelliSense wenig hilft, obwohl es gerade da sehr nützlich wäre.

Nach einigem Hin und Her entschieden wir uns für das Übel 1). Wir wählten z.B. für die Properties unserer eigenen Klassen in Triton den Einbuchstaben-Präfix z, weil eine Abklärung ergab, dass nur wenige .NET-Klassen bereits Properties haben, mit mit Z beginnen, und damit diese Properties nicht nur zusammen angezeigt werden in einer IntelliSense-Liste, sondern auch noch herausstechen.

Seit dieser Entscheidung haben wir eine rechte Menge Code gemäss diesen Präfix-Konventionen erzeugt, und es ist eine erste Beurteilung des Erreichten möglich. Der gewünschte Effekt bei IntelliSense hat sich eingestellt: Man behält so tatsächlich ohne Probleme den Überblick über die eigenen Dinge im Gegensatz zu den “fremden” Dingen des Frameworks. Aber hübsch sieht es irgendwie nicht aus, und eine richtige Gewöhnung an unsere mit Präfixen dekorierten Namen will sich eigentlich auch nicht einstellen.

Für jemanden, der sich im .NET-Framework wirklich gut auskennt, gäbe es vielleicht eine Wahlmöglichkeit 3), welche das Dilemma umgeht: ein Add-In programmieren, welches neben der IntelliSense-Liste eine zweite, entsprechend eingeschränkte Liste implementiert. Aber ganz soweit sind wir in der Megos noch nicht…

12. Januar 2009

Die vielen Implementationen von .NET

Gespeichert unter: Keine Kategorie — Schlagworte: — René Brunner @ 11:00

Hat man sich erst mal mit Hilfe einer virtuellen Maschine im Prinzip von Betriebssystem und CPU-Architektur gelöst, wie das ja bei .NET und der CLR der Fall ist, ist man frei, diese virtuelle Maschine für ganz unterschiedliche Umgebungen zu implementieren. Und obwohl .NET noch ziemlich jung ist, kommen schon eine ganze Reihe von Implementationen zusammen. Ich habe mir für dieses Posting einmal einen aktuellen Überblick verschafft (allerdings ohne aufzuführen, welche Komponenten aus dem umfangreichen .NET-Technologie-”Baukasten” denn genau wo zur Verfügung stehen):

Die allseits bekannte “Mutter” aller .NET-Implementationen und dementsprechend relativ uninteressant für diese Auflistung ist diejenige von Microsoft selbst für die verschiedenen Windows-Varianten, die hier zu Hause ist.

Eine Spur interessanter ist das .NET Compact Framework, ebenfalls von Microsoft selbst, das auf PDAs und Smartphones mit Windows Mobile läuft. Interessant, wie viel vom “grossen” Bruder es in die Kompakt-Variante schafft.

Es geht aber noch kleiner als Smartphones: Mit dem .NET Micro Framework versucht Microsoft, selbst Geräte mit stark limitiertem Speicher abzudecken, wie z.B. ihre experimentelle SPOT Armbanduhr zeigt.

Es ist mir nicht klar, ob die Shared Source Common Language Infrastructure, früher mit Rotor als Codename und in der Wikipedia hier beschrieben, eine zweite .NET-Implementation von Microsoft darstellt, oder nur eine neue Zusammenstellung einer einzigen Implementation, oder etwas dazwischen, aber interessant ist, dass frühe Versionen offenbar neben Windows auch auf FreeBSD und Mac OS X lauffähig waren.

Wer sich auch nur ein wenig dafür interessiert, wie die Welt ausserhalb von Microsoft aussieht, kennt sicher mittlerweile Mono, ein sehr erfolgreiches Projekt, welches es einem ermöglicht, seine .NET-Programme auch auf Linux und Mac OS X laufen zu lassen. Man kann offenbar, wie hier beschrieben, dank einem Hack Mono sogar verwenden, um Programme für das Compact Framework herzustellen, muss aber selbst aufpassen, nichts zu verwenden, was da nicht vorhanden ist.

Schon weniger bekannt dürfte sein, dass Mono im Bereich Open Source Konkurrenz hat in Form des DotGNU-Projekts. Ohne das Projekt im Detail zu kennen, bekam ich beim Studium der Website allerdings den Eindruck, dass hier im Moment um einiges kleinere Brötchen gebacken werden als bei Mono, und es scheint mit einem Motto GNU Freedom for the Net und Statements wie etwa Don’t get caught in a .Net auch stärker um ideologische Fragen zu gehen als bei der Konkurrenz.

Einen grossen Schub in Sachen Verbreitung könnte .NET erfahren, wenn es als Basistechnologie für Silverlight in der Version 2 den Weg in die Internet-Browser dieser Welt schafft. Microsoft will mit Silverlight 2 bereits selber eine Reihe von Plattformen abdecken, und das Mono-Pendant Moonlight will für eine nochmals grössere Reichweite sorgen. Wie erfolgreich die Sache wirklich wird, scheint mir im Moment noch etwas in den Sternen zu stehen, aber zumindest die Technologie sieht sehr solide aus.

Und jetzt wird es etwas exotisch: Die Firma Red Five Labs aus Südafrika hat, soweit erkennbar vollständig aus eigener Kraft, das .NET Compact Framework für Symbian/S60-basierte Smartphones implementiert. Wenn jetzt nur noch das Geld da wäre für eine ordentliche Werbekampagne, um dieses Produkt unter den Entwicklern auch bekannt zu machen… Und wie es damit weitergeht, wenn Silverlight 2 wie geplant ebenfalls auf S60 ankommt, ist eine offene Frage.

Wenn man mehr spielerisch veranlangt ist, kann man seine C#- und .NET-Kenntnisse auch dadurch erwerben, dass man mit Hilfe von XNA Spiele für die Xbox 360 programmiert. Sogar eine Unterstützung von Zune Players ist offenbar in der Pipeline.

Und zum Schluss noch dies: Unter dem Namen CrossNet hat offenbar jemand einen sehr speziellen CIL-Decompiler geschrieben, der Standard-C++-Code auswirft, den man dann fast überall kompilieren kann. Das ist für den eigenen Code ganz schön; das grosse und offenbar weitgehend ungelöste Problem scheint zu sein, wie man am “neuen Ort” zu einer .NET Base Class Library, sprich zu einem “Framework”, kommt.

20. Dezember 2008

Files via lokale HTML-Links öffnen

Gespeichert unter: Keine Kategorie — Schlagworte: , , — René Brunner @ 13:39

Visual Studio ist – unter vielem anderen – ein ganz passabler HTML-Editor, wenn man sich damit anfreunden kann, direkt in HTML zu arbeiten. Es muss ja nicht immer gleich eine Riesending wie Microsoft Expression Web oder Dreamweaver sein für Modifikationen an ein paar Seiten.

Eine Funktion habe ich allerdings so vermisst, dass ich sie per Makro nachgerüstet habe: eine Funktion, um einem lokalen HTML-Link zu folgen und das referenzierte File zu öffnen. Hat man z.B. im HTML-Text das folgende:

<a href=”sample.html”>

sollte es auf ganz einfache Weise möglich sein, dem Link zu folgen und das File sample.html in Visual Studio zu öffnen, auch wenn man kein Projekt hat, das dieses File enthält, weil man einfach mit einer Directory voller HTML-Files arbeitet.

Mit dem folgenden Makro braucht man nur den Cursor in den Filenamen zu positionieren und dann das Makro zur Ausführung zu bringen. (Am besten konfiguriert man natürlich eine Tastenkombination oder mindestens ein Icon in einer Toolbar hierzu.)


  Public Sub OpenFileAtCursor()
    Dim activeDoc As Document
    Dim sel As TextSelection
    Dim line As String
    Dim fileName As String
    Dim basePath As String
    Dim cursorColumn As Integer
    Dim startPos As Integer
    Dim endPos As Integer
    activeDoc = DTE.ActiveDocument
    sel = activeDoc.Selection
    cursorColumn = sel.AnchorPoint.LineCharOffset
    sel.SelectLine()
    line = sel.Text
    sel.Collapse()
    startPos = InStrRev(line, """", cursorColumn)
    endPos = InStr(cursorColumn, line, """")
    fileName = Mid(line, startPos + 1, endPos - startPos - 1)
    fileName = Replace(fileName, "/", "\")
    If Left(fileName, 1)  "\" Then
      basePath = Path.GetDirectoryName(activeDoc.FullName)
      fileName = basePath + "\" + fileName
    End If
    If File.Exists(fileName) Then
      DTE.ExecuteCommand("File.OpenFile", fileName)
    Else
      DTE.StatusBar.Text = "File """ + fileName + """ not found"
    End If
  End Sub

22. November 2008

Mehrfachvererbung

Gespeichert unter: Keine Kategorie — Schlagworte: , , — René Brunner @ 16:00

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…

15. November 2008

DataBinding und ArrayList

Gespeichert unter: Keine Kategorie — Schlagworte: , — René Brunner @ 11:36

An sich nur eine kleine Sache, aber ich beschreibe sie trotzdem mal kurz – vielleicht hilft’s dem einen oder anderen, das Problem schneller zu lösen:

Eine DataGridView, angesetzt per DataBinding auf eine ArrayList, die in meinem Programm immer funktioniert hatte, wollte plötzlich nicht mehr: Im Grid fehlten die Kolonnen. Keine Fehlermeldung, keine Exception, einfach keine Kolonnen mehr, das DataBinding versagte.

Der Grund war ganz einfach: Vor diesem Problem war es immer so gewesen, dass zum Zeitpunkt des Erzeugens der DataGridView sich mindestens 1 Element in der ArrayList befand. Jetzt war sie leer. Da ArrayList völlig untypisiert ist, hat ohne mindestens 1 Element in der Liste der DataBinding-Mechanismus absolut nichts, womit er arbeiten kann, und das Grid bekommt keine Kolonnen. (Und dass das Grid nach dem Auftauchen des ersten Elements die Bildung von Kolonnen nachholt, ist zugegebenermassen etwas viel verlangt.)

Eine Umstellung von ArrayList auf eine typisierte List<T> behob das Problem.

Bei einer Google-Suche nach dem Problem stiess ich noch auf ein mit bisher unbekanntes Interface ITypedList, beschrieben in diesem Blog-Eintrag. Damit scheint eine Feinkontrolle des DataBinding möglich zu sein; ich muss aber zugeben, dass ich die Sache noch nicht im Detail studiert habe.

Und dann noch dies: Es muss nicht immer Google sein. Selbst in den heutigen Zeiten gibt es Leute, welche fleissig Links sammeln und Web Directories pflegen. Wenn Sie mal ein paar neue Blogs zum Thema .NET probelesen wollen, finden Sie hier gegen 100 Stück – ich denke nicht, dass Sie die alle schon kennen…

Ältere Artikel »

Bloggen Sie auf WordPress.com.