从动态对象中动态获取值

9

标题听起来有点混乱,我来解释一下我的意思:假设你有一个C# 4.0的“动态”对象和一个属性的名称。如何从动态对象中检索该属性?

换句话说,你要如何实现:

public static object GetDynamicValue(dynamic o, string name) { ... }

另一种表述方法是,我正在尝试将一个动态对象视为IDictionary。
请注意,反射在这里可能不是一个选项,因为动态对象可以是自定义实现,不基于反射(例如通过扩展DynamicObject并执行自己的操作)。

1
你能多讲一些关于你的使用情况吗?如果你想要一个类似于IDictionary的动态对象,为什么不使用ExpandoObject呢?Jon的建议可能有效,但我感觉这对于你尝试做的事情来说是一个过于复杂的解决方案。 - Alexandra Rusina
2个回答

14
你需要构建一个调用站点,创建一个绑定器等操作。最简单的方法是编译这个代码:
public static object GetDynamicValue(dynamic o, string name)
{
    return o.Foo;
}

然后使用 Reflector 进行反编译并弄清楚它在干什么。不过要注意,这会相当复杂,而且你需要将其从单个、静态、缓存的调用站点更改为每次调用都创建一个新的站点。

以下是一个能够工作的示例... 但是否完全正确则是另一回事 :) (我通过完全按照上述建议所做来实现了这一点。)

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

class Test
{
    public static object GetDynamicValue(dynamic o, string name)
    {
        CallSite<Func<CallSite, object, object>> site 
            = CallSite<Func<CallSite, object, object>>.Create
            (Binder.GetMember(CSharpBinderFlags.None, name, 
             typeof(Test), new CSharpArgumentInfo[] 
             { CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null) }));
        return site.Target(site, o);
    }

    static void Main()
    {
        Console.WriteLine(GetDynamicValue("hello", "Length"));
    }
}

非常好,谢谢Jon!这带来了很多我没有处理过的概念,所以我需要仔细研究它。感觉奇怪的是在那里使用typeof(Test),因为它应该没有关系。但我尝试将其设置为null,代码仍然可以工作,所以显然它并没有做太多事情。 - David Ebbo
3
在可访问的成员方面,这很相关——使用此代码,您将能够访问Test的私有成员,但无法访问其他类型的私有成员。 - Jon Skeet

3
开源框架ImpromptuInterface(可在nuget中获取)可以实现这一点,并且会进行额外的工作,因为缓存调用站点对性能很重要。
请参见InvokeGet
public static object GetDynamicValue(dynamic o, string name) {
        return Impromptu.InvokeGet(o,name);
}

@David-Ebbo,虽然Clay允许您通过字符串名称访问其自身属性,但InvokeGet允许您通过字符串名称访问任何IDynamicMetaObjectProvider属性(或任何常规对象比反射更快)...除非我漏掉了什么。 - jbtule

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接