Dynamic Objects in C# 4.0
Eine neues Feature in C# 4 ist die „Dynamic Language Runtime“ (DLR). Mit dem „
dynamic“ Schlüsselwort ist es möglich zur Laufzeit Properties und Methoden an Objekten zu binden. Dabei gibt es allerdings keinerlei Überprüfung ob diese auch existieren – dafür erhöht sich die Flexibilität der Anwendung.
Eigene Klassen können durch die Implementierung des
DynamicObject und durch die Überschreibung der gewünschten Methoden erstellt werden.
Ein praktisches Beispiel dazu:Wer kennt das Problem nicht, es gibt eine Javascript Komponente die im Header der Seite einiger Einstellungen bedarf. Diese möchte man aber nicht per Javascript, sondern per Asp.Net setzen. Ist ja kein Problem, aber jede einzelne Property Serverseitig abzubilden kostet viel Zeit, insbesondere wenn die Komponente ständig weiterentwickelt wird.
Das Ausgabe sollte in JavaScript etwa so aussehen:
var myObject = new JavaScriptComponent();
myObject.width = 400;
myObject.show = false;
…Dynamic to the rescue!Wie oben beschreiben ist es möglich an dynamischen Objekten beliebige Properties und Methoden zu binden.
Dafür einfach eine Klasse ClientScriptCreator die von
DynamicObject ableitet.
public class ClientScriptCreator : DynamicObject Um die dynamischen Properties zu speichern wird ein
Dictionary verwendet
Dictionary<string, object> _members;Für die Benennung der clientseitigen Klasse und des Objekts werden zwei weitere Member deklariert
string _clientClassName;
string _clientObjectName;Die drei Member werden nun im Konstruktor der Klasse übergeben/gesetzt
public ClientScriptCreator(string clientClassName, string clientObjectName)
{
_members = new Dictionary<string,object>();
_clientClassName = clientClassName;
_clientObjectName = clientObjectName;
}Mit der Überschreibung der Methode
TrySetMember werden die Properties (falls noch nicht vorhanden) in das Dictionary gespeichert.
Diese liefert einen
SetMemberBinder und ein Objekt mit dem Wert.
public override bool TrySetMember(SetMemberBinder binder, object value)
{
if (!_members.ContainsKey(binder.Name))
{
_members.Add(binder.Name, value);
return true;
}
else
{
return false;
}
}Ähnlich dazu gibt es die
TryGetMember Methode, welche den gesetzten Wert (falls vorhanden) zurückgibt
public override bool TryGetMember(GetMemberBinder binder, out object result)
{
if (_members.ContainsKey(binder.Name))
{
result = _members[binder.Name];
return true;
}
result = null;
return false;
}Nun ist die Klasse im Stande dynamisch Properties zu setzen und zurück zu geben.
dynamic clientScriptCreator = new ClientScriptCreator();
clientScriptCreator.PropertyOne = "'Some string'";
clientScriptCreator.PropertyTwo = "false";
clientScriptCreator.PropertyThree = "4711";Das client Script muss nun zusammengesetzt werden um in den Header geschrieben zu werden.
Dafür eine Methode GetDefaultScript die den Dictionary Member durchläuft und diesen in ein <script type=“test/javascript“> Konstrukt wiedergibt.
private string GetDefaultScript()
{
StringBuilder sb = new StringBuilder();
sb.Append("<script type=\"text/javascript\">" + Environment.NewLine);
sb.Append(_clientObjectName + " = new " + _clientClassName + "();" + Environment.NewLine);
foreach (KeyValuePair<string, object> kvp in _members)
{
sb.Append(_clientObjectName + "." + kvp.Key + " = " + kvp.Value + ";" + Environment.NewLine);
}
sb.Append("</script>");
return sb.ToString();
}Das Script soll anschließend in einem Literal Control im head Bereich der Seite stehen
<head>
</asp:Literal ID="ClientScriptLiteral" runat="server">
</head>
Was noch fehlt ist der Aufruf der Methode, die das client Script zurückgibt.
Dafür die TryInvokeMember überschreiben, damit dynamische Methoden aufgerufen werden können. In dem folgenden Abschnitt wird auf die Methode „ToDefaultScript“ überprüft – wenn diese vorkommt, wird das Script zurückgeliefert:
public override bool TryInvokeMember(InvokeMemberBinder binder, object[] args, out object result)
{
if (binder.Name == "ToDefaultScript")
{
result = GetDefaultScript();
return true;
}
result = null;
return false;
}In der Page_Load der Seite erfolgt der Aufruf wie folgt
ClientScriptLiteral.Text = clientScriptCreator.ToDefaultScript();
Das Ganze ist nun wirklich dynamisch. Wird die JavaScript Komponente weiter entwickelt, braucht man sich nicht mehr darum zu kümmern und kann die Objekte wo gewünscht einfach erweitern.
Anbei gibt’s noch das Beispiel als Visual Studio 2010 Projekt dazu.