VS 2012 Code Maps

Es kommt immer wieder vor, dass man sich schnell einen Überblick über komplexe Strukturen verschaffen muss. Da kommen dann Themen, wie Vererbung, Projekthierarchien usw. ins Spiel und die ganze Angelegenheit wird schnell unübersichtlich. Mit Code Maps hat das Visual Studio nun ein weiteres Werkzeug, das man nutzen kann, um diesen Problemen zu begegnen.

VS-Projekt zum Download (7zip)

Screencast

Vorbemerkungen

Code Maps an sich sind ein alter Hut. Jeder, der schon einmal eine Anwendung geschrieben hat, in der z.B. Vererbung eine Rolle spielt, kommt früher opder später (meist eher später) an den Punkt, an dem er nicht mehr genau weiß, ob diese oder jene Klasse einen Aufruf auf diese oder jene Methode durchführt. Man hangelt sich dann i.d.R. einfach durch den Code oder sieht sich den Callstack an oder missbraucht irgendein anderes der vielen Werkzeuge des Visual Studio.

Das muss nun nicht mehr sein, denn Code Maps vereinfachen diesen Prozess erheblich.

Beispiel-Programm

Ich habe mir eine kleine Consolen-Anwendung ausgedacht, die das Thema demonstrieren soll. Sie nutzt einfach eine Klasse „Logger“, um irgendwelche Meldungen zu sammeln. Diese wiederum nimmt einen Meldungstext entgegen und macht daraus ein neues „LogItem“ welches wiederum von „ItemBase“ erbt. In „ItemBase“ selbst werden immer wieder Standard-Eigenschaften gesetzt. Im Visual Studio sieht die Anwendung ungefähr so aus:

Abb. 1: Sample-App im Solution Explorer
Abb. 1: Sample-App im Solution Explorer

Welche Möglichkeiten haben wir denn aber nun, um das Programm in seiner Tiefe kennen zu lernen?

Variante 1 ist, einfach F5 zu drücken und ein paar Haltepunkte zu setzen. Aus Erfahrung würden wir wahrscheinlich in der Main-Methode der Klasse „Program“ starten und dann mal sehen, wo wir landen.

Variante 2 ist schon etwas fortgeschrittener, kann aber nur auf Methoden angewandt werden: Sequenz-Diagramme. Klickt man z.B. mit der rechten Maustaste auf die Methode „Log“ in „Logger“ und wählt dann „Generate Sequence Diagram“, kommt sowas dabei heraus:

Abb. 2: Sequenz-Diagramm

Abb. 2: Sequenz-Diagramm

Das ist auf jeden Fall eine enorme Hilfe. Bedingung ist allerdings auch, dass man Sequenz-Diagramme lesen kann. Die Darstellung ist darüber hinaus nicht in jeder Situation informativ und vor allem zeigt das Sequenz-Diagramm immer nur einen Teilausschnitt, nämlich den, den wir als Startpunkt verwendet haben.

Es gibt wahrscheinlich noch weitere Techniken oder vielleicht sogar Tools, um die Struktur kennen zu lernen. Ich möchte die Gelegenheit jedoch nutzen und nun die Code Maps einsetzen.

Code Map

Ich möchte logisch genau da ansetzen, wo wir in unseren Überlegungen zuvor auch waren. Wir hatten festgestellt, dass die Klasse Program wahrscheinlich unser Ankerpunkt ist. Das haben wir jedoch nur deshalb getan, weil wir bereits über Hintergrundwissen zu Konsolen-Anwendungen verfügen. Ich denke, dass die meisten Leser mit mir darüber einig sein werden, dass dies in den meisten Projekten wohl kaum vorausgesetzt werden kann.

Aus diesem Grund möchte ich mit meinen Code Maps an einer anderen Stelle beginnen. Ich wähle die Klasse „Logger“, weil ich vermute, dass hier irgendwie was wichtiges passiert.

Ich öffne also die Klasse im Codefenster, klicke rechts auf den Namen der Klasse in


internal class Logger

und wähle „Show on Codemap“ (bei mir wäre Strg + ö der Shortcut). Das Ergebnis ist in Abb. 3 zu sehen.

Abb. 3: Codemap initial erzeugt
Abb. 3: Codemap initial erzeugt

Das ist auf den ersten Blick nicht so toll. Bewege ich aber den Mauszeiger über das „Logger“-Element, erscheint ein Pfeil, mit dem ich das Element expandieren kann:

Abb. 4: Expandiertes Logger-Element
Abb. 4: Expandiertes Logger-Element

Schon etwas besser. Ich kann nun unterlegt mit den typischen Member-Symbolen die Elemente der Klasse Logger einsehen. Ich sehe 3 Methoden, von der eine offensichtlich der Konstruktur ist („Logger“), eine Property („Items“) und eine Member-Variable „_items“.

Was aber viel wichtiger ist, als die Elemente selbst, sind die Pfeile zwischen ihnen. Klicke ich z.B. den Pfeil zwischen „Log“ und „_items“ an, erhalte ich Details zu dieser Beziehung zwischen den beiden:

Abb. 5: Beziehungsdetails
Abb. 5: Beziehungsdetails

Aha! Hier erkenne ich nun, dass die Methode „Log“ (Source Node) lesend (Category) auf das Feld (Category + Target Node) zugreift. Na, das ist doch schon sehr nützlich! Klicke ich die Beziehung zwischen der Methode „Logger“ (Konstruktor) und der Member-Variablen „_items“ an, erkenne ich, dass hier ein „Field Write“ erfolgt. Daraus kann ich schnell schließen, dass der Konstruktur die Member-Variable initialisert und dass diese dann von anderen Methoden genutzt wird.

Komplexer

Es wird Zeit, ein wenig tiefer in unser Projekt einzutauchen. Wie man sieht, gibt es ja noch die Klassen „LogItem“ und „ItemBase“. Ich wiederhole die Schritte aus dem vorhergehenden Absatz jeweils mit diesen Klasen und erhalte folgende Code Map:

Abb. 6: Code Map mit 3 Klassen
Abb. 6: Code Map mit 3 Klassen

Jetzt wird es schon interessanter. Ich kann sofort erkennen, dass „ItemBase“ durch „LogItem“ und „Logger“ genutzt wird. Ein Klick auf den Pfeil zwischen „LogItem“ und „ItemBase“ gibt mir sogar noch mehr Details:

Abb. 7: Angezeigte Vererbung
Abb. 7: Angezeigte Vererbung

„LogItem“ erbt also von „ItemBase“. Das ist eine entscheidende Information, das im Gegensatz dazu die Beziehung zwischen „Logger“ und „ItemBase“ nicht auf Ebene der Klasse, sonder der Methoden und Eigenschaften erfolgt. Klicke ich z.B. die Methode „Log“ in der Klasse „Logger“ an, sehe ich die Abhängigkeiten aus Sicht dieser Methode:

Abb. 8: Codemap für einzelne Methode
Abb. 8: Codemap für einzelne Methode

Die Methode hat 2 „References“-Bindungen. Eine auf „LogItem“ und die andere auf „ItemBase“. Ein Doppelklick auf die Methode bringt uns zu ihrer Implementierung:

/// <summary>
/// Adds a new message to the log.
/// </summary>
/// <param name="message">The message to log.</param>
public void Log(string message)
{
    _items.Add(new LogItem(message));
}

Die Pfeile sind aus dem „new LogItem..“ entstanden. Da die Methode also ein LogItem braucht und dieses wiederum von ItemBase erbt, erzeugt dies Bindungen zwischen der Methode und den beiden genannten Klassen.

Von Klein nach Groß

Ich habe meine Code Map bislang immer auf Klassenebene gestartet, was ich nicht muß. Ich hatte zum Schluss die Methode Logger.Log im Detail betrachtet. Nun schließe ich einfach meine komplexe Code Map, ohne sie zu speichern (dazu später mehr) klicke rechts auf den Methodennamen in meiner Klasse und wähle wiederum „Show on Code Map“:

Abb. 9: Start bei Methode "Log"
Abb. 9: Start bei Methode „Log“

Ein Rechtsklick auf dieses Element bringt ein Kontextmenu, aus dem ich „Show Methods this calls“ auswähle. Nach kurzem Nachdenken ändert sich die Code Map:

Abb. 10: Show Methods this calls
Abb. 10: Show Methods this calls

Die Methode ruft also den LogItem-Konstruktur und irgendwas außerhalb meiner Assembly auf. Letzteres kann ich mir wieder im Detail ansehen:

Abb. 11: Detail-Drilldown
Abb. 11: Detail-Drilldown

Mit anderen Worten: die Code Map macht auch vor Framework-Code nicht Halt und ich sehe daher wirklich alle Abhängigkeiten. Es gibt noch weitere Optionen im Kontextmenu einer Funktion. So liefert mir beispielsweise „Show fields this references“ zusätzlich alle Klassen-Felder, die diese Methode benötigt:

Abb. 12: Show fields this references
Abb. 12: Show fields this references

Das Ganze ließe sich noch weiter und weiter treiben, indem ich die gleichen Funktionen z.B. auf dem „LogItem“-Konstruktor noch einmal ausführe. Ich denke aber, die Idee ist bereits ganz gut rüber gekommen.

Teilen

Zunächst einmal kann man Code Maps als *.dgml-Dateien abspeichern. Das ist das Dateiformat, das Visual Studio standardmäßig für alle möglichen Formen von grafischen Darstellungen nutzt. Es basiert auf XML und kann auch Bestandteil der Solution und somit auch z.B. des TFS werden.

Möchte man über eine Code Map diskutieren, weil man z.B. irgendeine Sache nicht versteht oder meint, irgendein Aufruf ist sinnlos, kann man zunächst Kommentare an beliebige Elemente der Map heften. Einfach Rechtsklick auf das Element und dann „Add Comment“ fügt ein Bubble hinzu. Die Werte selbst werden über das Eigenschaften-Fenster geändert:

Abb. 1: Sample-App im Solution Explorer
Abb. 1: Sample-App im Solution Explorer

Den Kommentar in Abb. 13 habe ich der Kategorie „Fields“ hinzugefügt, daher das blaue Symbol rechts.

Außerdem kann man, ähnlich wie z.B. in Outlook, beliebige Elemente mit Flags versehen. Das färbt das Element ein und markiert es so besser. Hier mal ein sog. Follow-Up-Flag auf der Methode „Log“:

Abb. 14: Follow-Up-Flag
Abb. 14: Follow-Up-Flag

Ein Klick auf „Legend“ in der Symbolseite des Code-Map-Fensters erzeuigt eine Zeichen- und Symbolerklärung:

Abb. 15: Legende
Abb. 15: Legende

Schlussendlich kann man über den Button „Share“ das Ganze z.B. als Bild in einer Mail versenden:

Abb. 16: Share
Abb. 16: Share

Fazit

Wie so viele Beiträge in letzter Zeit, so zeigt auch dieser, dass Microsoft mit der neuen VS-Versionen einige extrem nützliche Features integriert hat. Sie sind teilweise gut versteckt und leider auch nicht allen VS-Versionen vergönnt. Das ändert aber erstmal nichts daran, dass vor allem auch im „Low-Level“-Bereich, also abseits von Azure, Mobile usw. genug zu entdecken und zu nutzen ist, das uns das Leben als Entwickler erheblich vereinfacht.

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert.

Diese Website verwendet Akismet, um Spam zu reduzieren. Erfahre mehr darüber, wie deine Kommentardaten verarbeitet werden.