动态地将成员添加到动态对象

时间:2010-01-17 03:34:32

标签: reflection dynamic .net-4.0 c#-4.0 dynamic-language-runtime

我正在寻找一种动态添加成员到动态对象的方法。好的,我想还需要一点澄清......

当你这样做时:

dynamic foo = new ExpandoObject();
foo.Bar = 42;

Bar属性将在运行时动态添加。但是代码仍然“静态地”引用到Bar(名称“Bar”是硬编码的)...如果我想在运行时添加属性而不知道它在编译时的名称会怎样?

我知道如何使用DynamicObject类的方法使用自定义动态对象(我实际上是blogged about it几个月前),但是如何使用任何动态对象?

我可以使用IDynamicMetaObjectProvider界面,但我不明白如何使用它。例如,我应该将哪个参数传递给GetMetaObject方法? (它需要Expression

顺便说一句,你如何对动态对象进行反射? “常规”反射和TypeDescriptor不显示动态成员......

任何见解都将受到赞赏!

4 个答案:

答案 0 :(得分:9)

你想要的是类似于Python的getattr / setattr函数。在C#或VB.NET中没有内置的等效方法。 DLR的外层(在Microsoft.Scripting.dll中附带IronPython和IronRuby)包含一组托管API,其中包括具有GetMember / SetMember方法的ObjectOperations API。您可以使用这些,但您需要DLR和基于DLR的语言的额外依赖。

可能最简单的方法是创建一个带有现有C#绑定器的CallSite。您可以通过查看ildasm或反射器中“foo.Bar = 42”的结果来获取此代码。但一个简单的例子就是:

object x = new ExpandoObject();
CallSite<Func<CallSite, object, object, object>> site = CallSite<Func<CallSite, object, object, object>>.Create(
            Binder.SetMember(
                Microsoft.CSharp.RuntimeBinder.CSharpBinderFlags.None,
                "Foo",
                null,
                new[] { CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null) }
            )
        );
site.Target(site, x, 42);
Console.WriteLine(((dynamic)x).Foo);

答案 1 :(得分:5)

开源框架Dynamitey将执行此操作(可通过nuget获得)。它封装了同时仍然缓存@ Dino-Viehland使用的调用站点和绑定器代码。

Dynamic.InvokeSet(foo,"Bar",42);

它也可以调用许多other kinds of c# binder too

答案 2 :(得分:5)

ExpandoObject实现IDictionary&lt; string,object&gt;虽然明确。这意味着您可以简单地将ExpandoObject强制转换为IDictionary&lt; string,object&gt;并操纵字典。

dynamic foo = new ExpandoObject();
foo.Bar = 42;
food = (IDictionary<string,object>)foo;
food["Baz"] = 54

答案 3 :(得分:1)

我知道这是一个很老的帖子,但我想我会传递Miron Abramson solution关于如何创建自己的类型并在运行时添加属性 - 以防其他人在那里寻找某些东西类似。