dynamic^2 oder ”variable Variablen”

(Irgendwie mag Windows Live Writer kein ² in der Überschrift…) Erzürnt

Seit .NET 4 gehört C# (und natürlich VB.Net) zu dem Kreis der dynamisch typisierten Sprachen. Dies ermöglicht die DLR, die Dynamic Language Runtime.
D.h. man kann Variablen “spät binden” (late binding), d.h. sie werden erst zur Laufzeit in ihren konkreten Typ materialisiert.
Das erlaubt einem eine hohe Flexibilität für verschiedenste Szenarien, wie z.B. beim COM Interop, der Kommunikation mit anderen dynamischen Sprachen für .NET (IronPython, IronRuby, IronJS, Phalanger, …) oder einfach in Situationen in denen der konkrete Typ erst zur Laufzeit bekannt ist (Reflection, XML, …).

Ich möchte jetzt nicht weiter auf die Grundlagen eingehen, da es inzwischen genügend Dokumentation dazu gibt. Einfach mal in die MSDN Doku reinschauen.

Basierend auf dem Beispiel aus der Doku mit dem DynamicDictionary möchte ich auf eine kleine Erweiterung eingehen: variable Variablen.
PHP Programmierer wissen vielleicht was ich meine. In PHP ist es beispielsweise möglich folgendes zu schreiben:

$variableName = "age";
$obj = new stdClass();
$obj->{$variableName} = "31";

echo $obj->age;

Also die Variable (bzw. den Member) variabel zu halten.

In C# ist das Out of the Box nicht möglich. Durch zwei Erweiterungsmethoden kann diese “dynamische Dynamik” aber leicht nachgereicht werden:

public static object GetMember(this object obj, string memberName)
{
    var binder = Binder.GetMember(
        CSharpBinderFlags.None, memberName, obj.GetType(),
        new[] { CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null) });
    var getterSite = CallSite<Func<CallSite, object, object>>.Create(binder);
    return getterSite.Target(getterSite, obj);
}

public static void SetMember(this object obj, string memberName, object value)
{
    var binder = Binder.SetMember(CSharpBinderFlags.None, memberName, obj.GetType(),
        new CSharpArgumentInfo[] {
            CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null),
            CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.Constant | CSharpArgumentInfoFlags.UseCompileTimeType, null)
        });
    var setterSite = CallSite<Func<CallSite, object, object, object>>.Create(binder);
    setterSite.Target.Invoke(setterSite, obj, value);
}

Somit ist es auch in C# möglich folgendes zu schreiben:

string variableName = "Age";
dynamic obj = new DynamicDictionary();
((object)obj).SetMember(variableName, 31);

Console.WriteLine(obj.Age);

Das Unboxing mittels ((object)obj).SetMember/GetMember… ist dabei notwendig, da es sich um eine Erweiterungsmethode handelt und diese für dynamische Objekte nicht aufgelöst werden.

Alternativ könnte man auch schreiben:

DynamicObjectExtensions.SetMember(
obj, variableName, 31);

Interessant ist, dass die beiden Befehle komplett unterschiedlichen IL Code generieren. Wo bei ersterem Befehl der Methodenaufruf SetMember steht, wird bei zweiterem Befehl SetMember wiederum dynamisch aufgerufen, was einen kleinen Performanceoverhead bedeutet. Man kann diesen jedoch wieder verschwinden lassen, indem man einfach obj nach object castet:

DynamicObjectExtensions.SetMember(
(
object)obj, variableName, 31);
Tagged , , ,

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: