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
メカニズムを使用する方法の例です。
- XMLの仕事
- ADO.Netのクエリ結果へのアクセス (他に誰か必要ですか?)
- ストアドプロシージャをラップします。
- プロパティの変更をキャプチャする
要するに、この不名誉を無条件に「標準的な」プログラミングと呼ぶことはできませんが、多くのユースケースがあります。 静的チェックの欠如は、文盲で使用されたときに検出できないバグの束になる可能性があると確信していますので、あなたへの私のアドバイスは注意することです!