Monthly Archives: May 2012

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 , , ,

Musste erst kürzlich ein PowerShell-Script über eine Batch-Datei aufrufen, welche sich im selben Ordner befindet. Mit folgendem Befehl kann dies gemacht werden:

 @echo off
 powershell.exe -File "%~dp0\<psscript>.ps1"

Innerhalb des PowerShell-Scriptes kann nun wiederum auf den relativen Pfad verwiesen werden:

 $parentPath = Split-Path -Parent $MyInvocation.MyCommand.Definition

Miscellaneous IT Pimpery

The %~dp0 (that’s a zero) variable when referenced within a Windows batch file will expand to the drive letter and path of that batch file.

View original post 129 more words