DynamicObjectについて知る

言語に新しい興味深い機能を追加するたびに、その機能を最大限に活用し始める人々が常に現れます。 DynamicObject



は単純で理解しやすいように見える機能ですが、遊び心のあるペンでは、より危険な仕事になります。



知人



まず、 System.Dynamic.DynamicObject



クラスが何であるかを見てみましょう。 このクラスは普通のようです-たとえば、このクラスから継承して、1つ以上のメソッドをオーバーロードできます。 ここに難しい方法がいくつかあります...詳しく見てみましょう。



まず、 DO



テストオブジェクトを作成し、 DynamicObject



から継承しDynamicObject







class DO : DynamicObject {}







ここで、 dynamic



キーワードを使用して、後悔することなくこのオブジェクトのメソッドを呼び出すことができます。



dynamic dobj = new DO();

dobj.NonExistentMethod();







私たちが得るものを推測します。 RuntimeBinderException



と呼ばれるRuntimeBinderException



とこのメッセージが表示されます。



'DynamicObjectAbuse.DO' does not contain a definition for 'NonExistentMethod'







当然です このクラスには、 NonExistentMethod()



メソッドがありません。 しかし、興味深いのは、それが決してありえないということです。 それDynamicObject



クラスにないプロパティとメソッドを呼び出す機能です。 コンパイル時ではないか、まったくありません。



存在しないメソッドの佐賀



これはどのように起こりましたか? 非常にシンプル-メソッドを呼び出すとき、実際にメソッドを呼び出します



bool TryInvokeMember(InvokeMemberBinder binder, object [] args, out object result)







NonExistentMethod()



メソッドの呼び出しの場合、このメソッドは引数なしで呼び出され、 binder



パラメーターには呼び出しに関する情報が含まれます。



{Microsoft.CSharp.RuntimeBinder.CSharpInvokeMemberBinder}

[Microsoft.CSharp.RuntimeBinder.CSharpInvokeMemberBinder]: {Microsoft.CSharp.RuntimeBinder.CSharpInvokeMemberBinder}

base {System.Dynamic.DynamicMetaObjectBinder}: {Microsoft.CSharp.RuntimeBinder.CSharpInvokeMemberBinder}

CallInfo: {System.Dynamic.CallInfo}

IgnoreCase: false

Name: "NonExistentMethod"

ReturnType: {Name = "Object" FullName = "System.Object" }







この場合、何らかの方法で処理できるメソッドの名前を取得します。 方法-それはあなた次第です。 任意のビジネスロジックを使用できます。 繰り返しますが、引数を受け取り( args



)、結果を返す( result



)ためのメカニズムがあります。 このメソッドは、すべてがうまくいった場合はtrue



返し、すべてがうまくいかなかった場合はfalse



返しtrue



。 このメソッドからfalse



、上で見た例外が発生します。



メソッド以外に何がありますか?



DynamicObject



のオーバーロードされた操作のセットDynamicObject



印象的です。 これは主に、存在しないプロパティへのアクセス、変換、単項演算子および二項演算子、インデックスを介したアクセスなどに応答する機能です。 一部の操作は、C#/ VBをまったく想定していません。たとえば、オブジェクトの作成のインターセプト、オブジェクトのメンバーの削除、インデックスによるオブジェクトの削除などです。



小さなインシデントが1つあります。 this



により、静的に型指定された動的DO



代わりに静的DO



オブジェクトが取得されます。 この問題の解決策は予測可能です。



private dynamic This { get { return this ; } }







これは、本当に必要な場合に使用します。 もちろん、 TryInvokeMember()



からThis



メソッドを呼び出すなどの愚かなことはしないでください。 StackOverflowException



ます。



Expandoobject



ExpandoObject



は通常、白鳥の歌です。 このクラスにより、ユーザーはメソッドとプロパティを任意に追加できます。



dynamic eo = new ExpandoObject();

eo.Name = "Dmitri" ;

eo.Age = 25;

eo.Print = new Action(() =>

Console.WriteLine( "{0} is {1} years old" ,

eo.Name, eo.Age));

eo.Print();







このようなオブジェクトをシリアル化することは、簡単な作業ではありません。 IDictionary



実装します。たとえば、アセンブリとインターフェイスの断片化に関連する非常に曖昧な理由により、XMLでシリアル化されないインターフェイスです。 関係ありません 本当に必要な場合は、 System.Runtime.Serialization.DataContractSerializer



を使用できます。



var s = new DataContractSerializer( typeof (IDictionary< string , object >));

var sb = new StringBuilder();

using ( var xw = XmlWriter.Create(sb))

{

s.WriteObject(xw, eo);

}

Console.WriteLine(sb.ToString());







当然、そのようなことはメソッドをシリアル化しません。 これを行うには、 DataContractResolver



中心にタンバリンを使用してダンスを整理できますが、このようなプラクティスはこの記事の目的ではありません。



それをどうしますか?



OK、一般に、機能はCOM開発の観点から理解できます。COM開発では、多かれ少なかれ深刻な相互作用はそれぞれ、オージェのstable舎をクリアすることに似ています。 動的言語との相互作用も良いプラスです。少なくともこのことに興味があれば、この記事ではこれらすべてのバインダーやその他のインフラの魅力について明確に説明します。



ここに、可能な限り引用されている素晴らしい例があります(したがって、これは盗作ではないことを望みます)。 XElement



、XMLを使用する場合、 XElement



要素と属性へのアクセスは単純に非人間的です。



var xe = XElement.Parse(something);

var name = xe.Elements( "People" ).Element( "Dmitri" ).Attributes( "Name" ).Value; // WTF?







これは単なる非人間的な構文です。 これは、はるかに「魅力的な」ソリューションです。最初に、 XElement



DynamicObject



コンテンツをその仮想プロパティでDynamicObject



するDynamicObject



を作成します。



public class DynamicXMLNode : DynamicObject

{

XElement node;

public DynamicXMLNode(XElement node)

{

this .node = node;

}

public DynamicXMLNode()

{

}

public DynamicXMLNode(String name)

{

node = new XElement(name);

}

public override bool TrySetMember(

SetMemberBinder binder, object value )

{

XElement setNode = node.Element(binder.Name);

if (setNode != null )

setNode.SetValue( value );

else

{

if ( value .GetType() == typeof (DynamicXMLNode))

node.Add( new XElement(binder.Name));

else

node.Add( new XElement(binder.Name, value ));

}

return true ;

}

public override bool TryGetMember(

GetMemberBinder binder, out object result)

{

XElement getNode = node.Element(binder.Name);

if (getNode != null )

{

result = new DynamicXMLNode(getNode);

return true ;

}

else

{

result = null ;

return false ;

}

}

}







さて、それは小さなこと次第です-プロパティが必要な場合は、単にポイントを介してそれを取ることができます:



var xe = XElement.Parse(something);

var dxn = new DynamicXmlNode(xe);

var name = dxn.people.dmitri.name;







モナドとAOP



繰り返しになりますが、このようなオブジェクトへのアクセスを制御する機能を備えているため、Unity.Interceptorまたは別のIoC + AOPフレームワークのスタイルでAOPを接続できます。 たとえば、上記の例では、チェーンの要素の1つが実際にnull



であってもNullReferenceException



を決して NullReferenceException



ないことを保証できnull



。 これを行うには、偽のオブジェクトを作成する必要がありますが、これは流なインターフェイスの中間クラスを作成することに似ています。



DSL'ki



もちろん、クラスで「何かを書く」能力は、原則として、静的にチェックできない(MPSスタイルの構文とは異なります)が、これに基づいてDSLを構築できるという考えに至りますが、難しいドメイン言語をいくつか説明してください。



「やめなさい」と言いますが、「文字列、ジェネレーター、その他のメタインフラストラクチャを使用する方が簡単ではありませんか?」実際、それはすべてあなたの見方に依存します。 たとえば、 DynamicXmlNode



は、 XMLがドメインであるDSLです。 同様に、たとえば次のように書くことができます。



myobject.InvokeEachMethodThatBeginsWithTest()







教訓は、 DynamicObject



InvokeEachMethod...



行を愚かにInvokeEachMethod...



それに応じて応答するということです。 この場合、反射を使用します。 もちろん、これはDSLとしてこの機能を使用することは、a)完全に文書化されておらず、第三者には理解できないことを意味します。 およびb)識別子の命名規則によって制限されます。 たとえば、次をコンパイルすることはできません。



DateTime dt = (DateTime)timeDO.friday.13.fullmoon;







ただし、 friday13



コンパイルは成功します。 ただし、既にJuly()



などの拡張メソッドが既にあり(おそらく本番4.July(2010)



で使用されていますJuly()



4.July(2010)



ような非常に不可解なコードを記述できます。 私にとっては、これはまったくクールではありません。



例へのリンク



以下は、 DynamicObject



な人々が彼らの地獄の目標のためにDynamicObject



メカニズムを使用する方法の例です。





要するに、この不名誉を無条件に「標準的な」プログラミングと呼ぶことはできませんが、多くのユースケースがあります。 静的チェックの欠如は、文盲で使用されたときに検出できないバグの束になる可能性があると確信していますので、あなたへの私のアドバイスは注意することです!



All Articles