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

Tho*_*que 8 reflection dynamic-language-runtime dynamic .net-4.0 c#-4.0

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

当你这样做:

dynamic foo = new ExpandoObject();
foo.Bar = 42;
Run Code Online (Sandbox Code Playgroud)

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

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

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

顺便说一句,你如何对动态对象进行反射?"定期"反思TypeDescriptor并不显示动态成员......

任何见解将不胜感激!

Din*_*and 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);
Run Code Online (Sandbox Code Playgroud)


jbt*_*ule 5

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

Dynamic.InvokeSet(foo,"Bar",42);
Run Code Online (Sandbox Code Playgroud)

它也可以调用许多其他类型的c#binder.


小智 5

ExpandoObject虽然显式地实现了IDictionary <string,object>.这意味着你可以简单地将ExpandoObject强制转换为IDictionary <string,object>并操纵字典.

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