Neuigkeiten in der .NET-Klassenbibliothek, Teil 1: Aufruferinformationen (Caller Info)

Der Dotnet-Doktor  –  2 Kommentare

Die Version 4.5 der .NET-Framework-Klassenbibliothek (.NET Framework Class Library – FCL) enthält 947 neue Klassen. Davon entfallen jeweils mehr als 10 oder mehr neue Klassen auf die 13 in der folgenden Tabelle genannten FCL-Namensräume.

Namensraum Anzahl neuer Klassen Bemerkung
System.IdentityModel 216 Windows Identity Foundation (WIF
System.Web 150 Neue Klassen für ASP.NET Webforms und ASP.NET MVC
System.Windows 143 Neue Klassen für WPF, insbesondere das Ribbon-Steuerelement
System.Activities 81 Neue Klassen für Workflow
System.Net 76 Neue Klassen für HTTP-Netzwerkprogrammierung sowie Websockets
System.ServiceModel 74 Neue Klassen für WCF
System.ComponentModel 38 Neue Klassen für Datenannotationen
System.Runtime 36 Neue Klassen für Interoperabilität mit WinRT, und Ladeprofiloptimierung
Microsoft.Build 28 Neue Klassen für msbuild.exe
System.Data 26 Neue Klassen für ADO.NET Entity Framework
System.Diagnostics 24 Neue Klassen für Event Tracing for Windows (ETW)
System.Security 15 Klassen für Windows Identity Foundation (WIF)
System.Reflection 10 u.a. neue Klassen für Assemby-Metadaten

Insgesamt steigert sich damit die Anzahl der beim .NET mitgelieferten Klassen auf 13.524.

Klassenstatistik .NET 1.0 bis 4.5

In den kommenden Wochen werde ich in einer Artikelserie zehn Neuerungen in der .NET-Klassenbibliothek vorstellen, die nicht die großen Bibliotheken WCF, WPF, Entity Framework, WF, WIF, ASP.NET und TPL betreffen und daher nicht so im Licht der Öffentlichkeit stehen.

Neuigkeiten in der .NET-Klassenbibliothek, Teil 1: Aufruferinformationen (Caller Info)

Gelegentlich besteht der Bedarf, dass eine Methode ihren Aufrufer kennt, z. B. für Ablaufverfolgung und Diagnosezwecke. Seit .NET 1.0 gibt es die Möglichkeit, den sogenannten Stacktrace abzurufen:

System.Diagnostics.StackTrace t = new System.Diagnostics.StackTrace();
foreach (var f in t.GetFrames())
{
Console.WriteLine(f);
}

Dabei ist es jedoch immer etwas mühsam, den direkten Aufrufer herauszufiltern. Neu in der .NET-Klasssenbibliothek ist nun, dass durch .NET-Attribut eine Methode diese Aufruferinformationen direkt vom Compiler als Parameter erhält (vgl. __FILE__ und __LINE__ in C++). Die folgende Abbildung zeigt, wie man diese speziellen Parameter definiert. Sie erscheinen als optionale Parameter für den Aufrufer. Dieser muss sie jedoch zwingend leer lassen, damit der Compiler sie befüllen kann. Besser wäre es gewesen, wenn Visual Studio diese Parameter ganz ausblenden würde.

Aufruferinformationen durch Caller-Info-Attribute
Ausgabe des obigen Beispiels

Da die Injizierung der Werte durch den Compiler erfolgt und nicht wie beim Stacktrace zur Laufzeit ermittelt wird, sieht man bei den Caller-Info-Attributen auch nach einer Obfuskation noch die ursprünglichen Methodennamen. Das bedeutet aber dann auch: Caller-Info-Attribute schwächen die Obfuskation, denn im Deompiler sieht man dennoch den Namen der aufrufenden Methoden:

int fhiuahfdheiadfgzaegfegfkhgetfwdhjadslf = 
this.adfgzaegfegfkhgetfwdhjad (10, "Run",
"h:\\TFS\\Demo\\NET45\\NET45Demos\\Konsole45CS
\\FCL\\CallerInformer.cs", 21);

Es gibt keinen Unterschied zwischen Debug- und Release-Kompilierung.

Sinnvoll sind die Caller-Info-Attribute für die Realisierung des INotifyPropertyChanged-Mechanismus. Bisher musste jede Property im Setter einen eigenen Namen an das Ereignis übergeben. Nun kann man das in einer Datenklasse eleganter lösen, in dem eine Hilfsroutine in der Klasse das CallerMemberName-Attribut verwendet, siehe das Beispiel:

public void Run3()
{
var d = new Datenobjekt();
d.PropertyChanged += delegate { Console.WriteLine
("Wert geändert!"); };
d.Wert = 10;

}

...

class Datenobjekt : System.ComponentModel.INotifyPropertyChanged
{
public event System.ComponentModel.PropertyChangedEventHandler
PropertyChanged;

/// <summary>
/// Realisierung in .NET 4.5, ohne dass der Aufrufer den
/// Property-Namen übergeben muss
/// </summary>
/// <param name="propertyName"></param>
private void NotifyPropertyChanged([CallerMemberName]
String propertyName = "")
{
if (PropertyChanged != null)
{
PropertyChanged(this, new
System.ComponentModel.PropertyChangedEventArgs(propertyName));
}
}

private int wert;

public int Wert
{
get { return wert; }
set { wert = value; NotifyPropertyChanged(); }
}
}