web-dev-qa-db-ja.com

別の変数に名前があるときにC#4動的オブジェクトのプロパティを設定する方法

dynamic C#4.0オブジェクトのプロパティを、実行時にしかわからないプロパティの名前で変更する方法を探しています。

次のようなことを行う方法はありますか(ExpandoObjectは例として使用されています。これはIDynamicMetaObjectProviderを実装する任意のクラスです):

string key = "TestKey";
dynamic e = new ExpandoObject();
e[key] = "value";

これは次と同等です:

dynamic e = new ExpandoObject();
e.TestKey = "value";

または、前方反射の唯一の方法ですか?

43
Kieran Benton

それほど簡単ではありませんReflectionは、notdynamicの全範囲である通常の型モデルを想定しているため、機能しません。実際に通常のオブジェクトと話しているだけの場合は、ここでリフレクションを使用してください。そうでなければ、コンパイラが基本的な割り当てのために出力するコードをリバースエンジニアリングし、柔軟にメンバー名を持つように微調整することを期待するかもしれません。正直言って、これは魅力的なオプションではありません。シンプルな:

dynamic foo = ...
foo.Bar = "abc";

に翻訳する:

if (<Main>o__SiteContainer0.<>p__Site1 == null)
{
    <Main>o__SiteContainer0.<>p__Site1 = CallSite<Func<CallSite, object, string, object>>.Create(Binder.SetMember(CSharpBinderFlags.None, "Bar", typeof(Program), new CSharpArgumentInfo[] { CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null), CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.Constant | CSharpArgumentInfoFlags.UseCompileTimeType, null) }));
}
<Main>o__SiteContainer0.<>p__Site1.Target(<Main>o__SiteContainer0.<>p__Site1, foo, "abc");

動的オブジェクトと非動的オブジェクトの両方で機能するアプローチが必要な場合: FastMember はこれに便利で、タイプまたはオブジェクトレベルで機能します。

// could be static or DLR 
var wrapped = ObjectAccessor.Create(obj); 
string propName = // something known only at runtime 
Console.WriteLine(wrapped[propName]);

nugetで利用でき、動的および非動的シナリオの両方に最適化されています。

16
Marc Gravell

Paul Sasikは C#4.0 Dynamic vs Expando ...どこに収まりますか? で同様の質問に回答しました

using System;
using System.Dynamic;

class Program
{
    static void Main(string[] args)
    {
        dynamic expando = new ExpandoObject();
        var p = expando as IDictionary<String, object>;
        p["A"] = "New val 1";
        p["B"] = "New val 2";

        Console.WriteLine(expando.A);
        Console.WriteLine(expando.B);
    }
}
60
Jonas Follesø

Jonasの答えに追加するために、新しいvar pを作成する必要はありません。代わりにこのアプローチを使用してください。

using System;
using System.Dynamic;

class Program
{
    static void Main(string[] args)
    {
        dynamic expando = new ExpandoObject();
        ((IDictionary<String, object>)expando)["A"] = "New val 1";
        ((IDictionary<String, object>)expando)["B"] = "New val 2";

        Console.WriteLine(expando.A);
        Console.WriteLine(expando.B);
    }
}
9
Jazimov

私のオープンソースフレームワーク Dynamitey には、DLRを使用して文字列名に基づいて呼び出すためのメソッドがあります。バインディングサイトをキャッシュし、1つのメソッド呼び出しまで効率化します。また、非動的オブジェクトのリフレクションよりも高速に実行されます。

Dynamic.InvokeSet(e, "TestKey", "value");
5
jbtule

fast-member は法案に合うかもしれません-その場でILを生成するように見えますが、最初の使用後に本当に高速になるようにそれをキャッシュします。

0
Marc Clifton