web-dev-qa-db-ja.com

実行時に動的オブジェクトにメンバーを追加する

.NET 4.0のDynamicObjectモデルを調査しています。アプリケーションは、ある種のtext/xmlファイルを通じてオブジェクトが記述されるものであり、プログラムはそのファイルを読み取ったときにオブジェクトを作成する必要があります。

DynamicObjectを使用すると、事前にメンバーの名前がわかっているため、メンバーを簡単に追加できます。しかし、追加するメンバーの名前さえわからない場合はどうでしょうか?それを同様に動的にする方法はありますか?

たとえば、テキスト/ XMLファイルで記述されているように、メンバー 'Property1'、 'Property2'、および 'PropertyA'および 'PropertyB'を持つオブジェクトを作成する必要があるとします。この情報に基づいてオブジェクトを動的に作成するにはどうすればよいですか?

[〜#〜] update [〜#〜]この投稿からアイデアを得ました: http://www.codeproject.com /KB/cs/dynamicincsharp.aspx

この実装により、次のようなことができます。

dynamic d = new PFDynamicChannel();
PFCouplings c = ((PFChannel)d).Coupling;
d.NewProperty = "X";

辞書を使用したくないのは、TryGetMemberメソッドと、TrySetMemberメソッドをオーバーライドして、そのメソッド内でプログラムに不可欠なイベントを発生させるためです。

この方法で、基本クラス(PFChannel)から継承できますが、その場でメンバーを追加することもできます。しかし、私の問題は、実行時まで新しいプロパティ名を知らないことです。そして、実際には、動的オブジェクトによって新しいプロパティをその場で追加できるとは思いません。この場合、ExpandoObjectを使用してこの機能を実現するにはどうすればよいですか?

29
sbenderli

あなたがonlyだけを行う必要がある場合、 ExpandoObject を見てください。それを行う必要がある場合まだDynamicObjectを使用する場合、基本的にプロパティ値を記憶するコードを書く必要があります...埋め込みExpandoObjectで潜在的に行うことができます。

しかし、後でこのオブジェクトで何をしたいのかは明確ではありません-動的な型指定が本当に必要ですか? Dictionary<string, object>実際にはもっと悪くなる?基本的に後でオブジェクトを消費するものに依存します。

45
Jon Skeet

これによると、 ExpandoObjectへのプロパティとメソッドの動的な追加!

... expandoオブジェクトをバリューホルダーとして使用し、動的に名前が付けられたプロパティを追加するときに、IDictionary <string、object>にキャストできます。

dynamic myobject = new ExpandoObject();

IDictionary<string, object> myUnderlyingObject = myobject;

myUnderlyingObject.Add("IsDynamic", true); // Adding dynamically named property

Console.WriteLine(myobject.IsDynamic); // Accessing the property the usual way

これはテストされており、コンソール画面に「true」と出力されます。

もちろん、基礎となるオブジェクトが別のクラスから継承する必要がある場合、この例は、潜在的なカスタム実装のアイデアを示すためだけに提供されています。

クラス実装にexpandoオブジェクトを含め、trygetとtrysetの呼び出しをクラスのexpandoオブジェクトのインスタンスにリダイレクトするのでしょうか?

[〜#〜] update [〜#〜]

基本クラスがDynamicObjectから派生する場合(つまり、すべてのTrySet/Get/Invokeメソッドをオーバーライドできることを意味します)、辞書を内部で使用することもできます。 try get/setオーバーライドでは、必要なイベントを起動し、設定を内部辞書に委任します。

新しいプロパティを追加(または既存のプロパティを削除)するには、TryInvokeをオーバーライドできます。 mothod名がたとえば「AddProperty」で、string型の引数が1つある場合、引数の名前で新しいアイテムを辞書に追加します。同様に、「RemoveProperty」などを動的に定義します。expandoオブジェクトも必要ありません。

class MyBaseClass: DynamicObject
{
    // usefull functionality
}

class MyClass: MyBaseClass
{
    Dictionary<string, object> dynamicProperties = new Dictionary<string, object>();

    override bool TryGetMember(...)
    {
       // read the value of the requested property from the dictionary
       // fire any events and return
    }

    override bool TrySetMember(...)
    {
       // set the value of the requested property to the dictionary
       // if the property does not exist,
       // add it to the dictionary (compile time dynamic property naming)
       // fire any events
    }

    override bool TryInvoke(...)
    {
       // check what method is requested to be invoked
       // is it "AddProperty"??
       // if yes, check if the first argument is a string
       // if yes, add a new property to the dictionary
       // with the name given in the first argument (runtime dynamic property naming)
       // if there is also a second argument of type object,
       // set the new property's value to that object.

       // if the method to be invoked is "RemoveProperty"
       // and the first argument is a string,
       // remove from the Dictionary the property
       // with the name given in the first argument.

       // fire any events
    }
}

// USAGE
static class Program
{
    public static void Main()
    {
        dynamic myObject = new MyClass();

        myObject.FirstName = "John"; // compile time naming - TrySetMember
        Console.WriteLine(myObject.FirstName); // TryGetMember

        myObject.AddProperty("Salary");  // runtime naming (try invoke "AddProperty" with argument "Salary")
        myObject.Salary = 35000m;
        Console.WriteLine(myObject.Salary); // TryGetMember

        myObject.AddProperty("DateOfBirth", new DateTime(1980,23,11)); // runtime naming (try invoke "AddProperty" with fisrt argument "DateOfBirth" and second argument the desired value)
        Console.WriteLine(myObject.DateOfBirth); // TryGetMember

        myObject.RemoveProperty("FirstName"); // runtime naming (try invoke "RemoveProperty" with argument "FirstName")

        Console.WriteLine(myObject.FirstName); // Should print out empty string (or throw, depending on the desired bahavior) because the "FirstName" property has been removed from the internal dictionary.

    }
}

もちろん、私が言ったように、基本クラスがDynamicObjectから派生している場合にのみ機能します。

33

この場合、動的オブジェクトを使用したいかどうかはわかりません。

c#のdynamicを使用すると、次のようなことができます。

  dynamic something = GetUnknownObject();
  something.aPropertyThatShouldBeThere = true;

ExpandoObject を使用すると、次のことができます。

  var exp = GetMyExpandoObject();
  exp.APropertyThatDidntExist = "something";

どちらも、コンパイル時にプロパティ名を実際に存在するかのように使用できます。あなたの場合、この構文を使用する必要はまったくないので、それがどんな利点をもたらすかはわかりません。代わりに、単にDictionary<string, object>、および:

var props = new Dictionary<string, object>();
   foreach( var someDefinintion in aFile)
   {
      props[ someDefinition.Name] = someDefinition.value;
   }

Dictionary<string,object>は基本的にexpandoオブジェクトです-しかし、異なる構文をサポートしています。はい、私は単純化しています-しかし、これを他のコードから、またはバインディング/などを使用していない場合、これは基本的に正しいです。

7
Philip Rieck

「基本クラスがDynamicObjectから派生している場合」というバリアントに関するThanasis Ioannidisの回答に関連して、より簡単な方法があります。次のように「MyClass」クラスにメソッドAddPropertyを追加するだけです。

    class MyClass: MyBaseClass
    {
        Dictionary<string, object> dynamicProperties = new Dictionary<string, object>();

        public void AddProperty(string key, object value){
            dynamicProperties[key] = value;
        }

その後、使用することができます

dynamic myObject = new MyClass();
myObject.AddProperty("DateOfBirth", new DateTime(1980,23,11));

「TryInvoke」へのオーバーライドは必要ありません。

1
Marko Rakić

dynamicタイプは、基本的にプロパティバッグとして実装されます。これは、キー(プロパティ名)とオブジェクト(ここではタイプセーフでない方法)の辞書を意味します。この辞書に直接アクセスできる場合はIamは使用しませんが、自分で辞書を使用できます。

くそー、ジョン・スキートは速かった:(

0
Euphoric