8

ExpandoObject を使用できず、自分自身をそのようにロールバックする必要があると仮定します:-

class MyObject : DynamicObject {
    dictionary<string, object> _properties = dictionary<string, object>();

    public override bool TryGetMember(GetMemberBinder binder, out object result) {
        string name = binder.Name.ToLower();

        return _properties.TryGetValue(name, out result);
    }

    public override bool TrySetMember(SetMemberBinder binder, object value) {
        _properties[binder.Name.ToLower()] = value;

        return true;
    }
}

そして、私が持っているクラス階層をさらに下ります

class MyNewObject : MyObject {
    public string Name {
        get {
            // do some funky stuff
        }
        set {
            // ditto
        }
    }
}

次のことができるようになったので、これは非常に素晴らしいです:-

dynamic o = MyNewObject();

o.Age = 87;     // dynamic property, handled by TrySetMember in MyObject
o.Name = "Sam"; // non dynamic property, handled by the setter defined in MyNewObject

しかし、上記は、コンパイル時にプロパティ (年齢、名前など) を知っていることを前提としています。

実行時までそれらがどうなるかわからないとします。

上記を変更して、実行時にしかわからないプロパティをサポートするにはどうすればよいですか?

基本的に、TrySetMember を呼び出すコードを直接呼び出して、新しいプロパティを作成するか、定義されている場合はゲッター/セッターを使用する方法を尋ねていると思います。

次のような最終的な解決策:-

using System.Dynamic;
using Microsoft.CSharp.RuntimeBinder;
using System.Runtime.CompilerServices;

class MyObject : DynamicObject {
    Dictionary<string, object> _properties = new Dictionary<string, object>();

    public object GetMember(string propName) {
        var binder = Binder.GetMember(CSharpBinderFlags.None,
              propName, this.GetType(),
              new List<CSharpArgumentInfo>{
                       CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null)});
        var callsite = CallSite<Func<CallSite, object, object>>.Create(binder);

        return callsite.Target(callsite, this);
    }

    public void SetMember(string propName, object val) {
        var binder = Binder.SetMember(CSharpBinderFlags.None,
               propName, this.GetType(),
               new List<CSharpArgumentInfo>{
                       CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null),
                       CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null)});
        var callsite = CallSite<Func<CallSite, object, object, object>>.Create(binder);

        callsite.Target(callsite, this, val);
    }

    public override bool TryGetMember(GetMemberBinder binder, out object result) {
        string name = binder.Name.ToLower();

        return _properties.TryGetValue(name, out result);
    }

    public override bool TrySetMember(SetMemberBinder binder, object value) {
        _properties[binder.Name.ToLower()] = value;

        return true;
    }
}
4

2 に答える 2

10

C# コンパイラは、動的なキーワードの使用を、文字列名を使用した dlr による呼び出しに変換していますが、これらの API は、コンパイラの助けなしに直接使用することは困難です。オープン ソース フレームワークのDynamitey (nuget から PCL ライブラリとして入手可能) は dlr API をカプセル化して、Impromptu.InvokeSet(target,name,value) を呼び出すだけで簡単にできるようにします。

using Dynamitey;
...

dynamic o = MyNewObject();

Dynamic.InvokeSet(o,"Age" ,87); 
Dynamic.InvokeSet(o,"Names" ,"Same);   

ゲッターとセッターは、実際の Microsft API を直接使用するのに最も複雑ではないため、サードパーティのフレームワークを使用したくない場合は、ソースにアクセスすることもオプションです。

using Microsoft.CSharp.RuntimeBinder;
using System.Runtime.CompilerServices;
...

dynamic o = MyNewObject();
var binder = Binder.SetMember(CSharpBinderFlags.None,
                   "Age",
                   typeof(object),
                   new List<CSharpArgumentInfo>{
                           CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null),
                           CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null)
                                               });

  var callsite = CallSite<Func<CallSite, object, object, object>>.Create(binder);

  callsite.Target(callsite,o,87);

  var binder2 =Binder.SetMember(CSharpBinderFlags.None,
                   "Name",
                   typeof(object),
                   new List<CSharpArgumentInfo>{
                           CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null),
                           CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null)
                                               });
  var callsite2 = CallSite<Func<CallSite, object, object, object>>.Create(binder2);

  callsite2.Target(callsite2,o,"Sam");
于 2012-08-22T02:04:59.313 に答える
6

しかし、上記は、コンパイル時にプロパティ (年齢、名前など) を知っていることを前提としています。

実行時までそれらがどうなるかわからないとします。

その場合、C# 4 の動的型付けはまったく役に立ちませんDictionary<string, object>

それが答えであると仮定するのではなくdynamic、要件をよく見て、実際に達成しようとしていることを解決することをお勧めします。明確に定義された一連の要件を取得すると、それらを実装するのがより簡単になります。

実装も同様に行う必要があることに気付くかもしれません...ただし、他のクラスを派生させ、それらのプロパティも辞書を介して公開したい場合、それはよりトリッキーになるという問題があります。MyObjectIDictionary<string, object>ExpandoObjectMyObject

于 2012-08-21T14:55:48.210 に答える