Wo kommt der Aufruf her?
Diese Frage ist insbesondere bei Ausnahmeverhalten einer Anwendung spannend. Der Debugger in Visual Studio bietet natürlich viele Möglichkeiten den Aufrufpfad zurückzuverfolgen. Von der Ansicht des StackTraces bis hin zu Intellitrace steht dem Entwickler eine breite Palette zur Inspektion bereit. Doch warum ist es so schwierig, einfach den Aufrufer namentlich benannt zu bekommen?
Ist es gar nicht! Mit .NET 4.5 haben ein paar neue Attribute in das Framework Einzug erhalten, die dies durch einen kleinen technischen Trick ermöglichen.
Die neue Funktionalität ist unter folgendem Namespace zu finden:
System.Runtime.CompilerServices
Bereits der Name der Attribute lässt erahnen, dass über sie Informationen des Aufrufers (Caller) zur Verfügung gestellt werden. Es handelt sich konkret um folgende Attribute:
- CallerFilePath,
- CallerLineNumber und
- CallerMemberName
Um die neue Funktionalität nutzen zu können, muss man den Methoden, in denen man die Informationen über den Aufrufer benötigt, optionale Parameter hinzufügen. Diese Parameter müssen dann noch mit dem entsprechenden Attribut versehen werden, um die Informationen zu erhalten. In C# sieht das dann wie in Abbildung 1 aus.
Wie im dargestellten Quellcode anhand der Methode TraceMessage beispielhaft dargestellt ist, können diese Parameter in Methoden, welche für das Tracing zuständig sind, besonders hilfreich sein. Hier kann auf die Parameter wie gewohnt zugegriffen werden, ohne dass man sich beim Entwickeln des Aufrufers darum kümmern muss (vgl. Abbildung 2).
Wie in Abbildung 2 dargestellt, wird beim Aufrufen der Methode TraceMessage lediglich der obligatorische Parameter übergeben. Den Rest der Arbeit übernimmt der Compiler. Ein Blick in den IL-Code bringt die Technik zum Vorschein (vgl. Abbildung 3).
Der Compiler fügt die Werte als Zeichenkettenliterale in den IL-Code ein. Deshalb weicht der IL-Code des Aufrufers vom ursprünglichen Code ab. In Abbildung 3 sind Name des Aufrufers, zugehörige Quellcodedatei und Zeilennummer der aufrufenden Stelle hervorgehoben. Auch ist zu erkennen, dass die optionalen Parameter explizit übergeben werden.
Die Tatsache, dass die Informationen in den IL-Code eingefügt werden, bringt gleich mehrere Vorteile mit sich. Zum einen sind sie vor Maßnahmen der Obfuskierung sicher. Somit kann man auch bei einem verschleierten Code mit den Ausgaben noch die richtigen Codestellen ausfindig machen. Zum anderen werden die Daten beim Refactoring automatisch mit dem nächsten Buildvorgang mit gepflegt.
Der zuletzt genannte Punkt kann auch noch in einem ganz anderen Szenario helfen. Wenn man eine Anwendung nach dem MVVM-Pattern auf .NET-Basis entwickelt, entsteht typischerweise ein ViewModel, welches das INotifyPropertyChanged-Interface implementiert. Bis zur Version 4.0 des .NET Frameworks musste man den Namen des geänderten Properties als String-Literal im SourceCode verankern. Dieses Vorgehen funktioniert zwar technisch, birgt aber das Risiko, dass dieser Name beim Refactoring nicht mit verändert wird. Mittels des Attributs CallerMemberName kann man die Angabe des Namens an den Compiler abgeben. Dadurch hat man einen manuellen Schritt und damit eine potentielle Fehlerquelle weniger im Source Code.
Die neuen Caller Information im .NET Framework 4.5 bieten in verschiedenen Anwendungsfällen eine Vereinfachung. Der Entwickler bekommt eine Aufgabe abgenommen, die nun der Compiler für ihn übernimmt. Wieder ein kleines Stückchen Effizienzgewinn durch die Weiterentwicklung der verwendeten Technologien.