【问题标题】:Complex anonymous objects to dynamic public properties动态公共属性的复杂匿名对象
【发布时间】:2013-04-23 15:28:49
【问题描述】:

如何在dynamic 对象中公开匿名对象的复杂结构?

匿名对象被标记为internal,所以我正在寻找一种创造性的方法来解决这个问题

// This is the library I control
public void SendObject() {
    var anonymous = new {
        Text = "Test",
        SubItem = new {
            SubText = "Bla",
            SubSub = new {
                SubSubText = "Baha"
            }
        }
    };
}

dynamic dyn = ExposeAnonymous(anonymous); // Perform voodoo

var result = ExternalLibrary.GetSpecialProperty(dyn);

// External library I don't control
public object GetSpecialProperty(dynamic dyn) {
    return dyn.SubItem.SubSub.SubSubText;
}

问题是在将dynamic 发送到其他外部库时,我无法控制,您会收到如下错误:

“对象”不包含“子项”的定义。

【问题讨论】:

  • 在这里也可以工作(在将第 4 行的分号替换为 , 之后)
  • 关键部分是“其他库”——当然在 sn-p 中没有展示...

标签: c# dynamic anonymous-types


【解决方案1】:

问题在于将动态发送到其他库时,

...问题来了。 C# 编译器将匿名类型声明为internal,这意味着其他程序集无权访问它们。

要么停止使用匿名类型,要么使用[InternalsVisibleToAttribute] 使该类型对其他程序集可见。在包含创建匿名类型实例的类型的程序集中,使用:

[InternalsVisibleTo("ExternalLibrary")]

(我实际上希望问题出在SubItem 而不是SubSub...)

【讨论】:

  • 这是一个(错误的?)库,我无法控制,所以 InternalsVisibleToAttribute 不起作用。
  • @SebNilsson:什么是你无法控制的库?返回对象的那个,还是消耗它的那个?如果你不能改变返回对象的那个,你可能需要求助于反射。 (如果您没有得到适当的信任,即使这样也行不通。)
  • 我认为这个问题可能过于断章取义,我希望得到一个快速修复。它基本上是 RazorEngine 库,它存在来自匿名类型的动态问题。但是对于许多不同的数据结构“停止使用匿名”并不是一种选择。
  • @SebNilsson:那么您可以控制哪个库?老实说,你仍然不是很清楚。
  • @SebNilsson:是的,那么为什么不能添加InternalsVisibleTo 以便RazorEngine 可以看到您的类型?事实上,是 真的 RazorEngine 正在使用它,还是它是您定义的一些代码,但 RazorEngine 是 使用 的?您应该编辑您的问题以提供很多更多详细信息。
【解决方案2】:

匿名类型是internal,DLR 在运行时执行与编译器在编译时执行相同的可访问性分析。因此,您无法使用 dynamic 从另一个程序集访问匿名类型的成员。

一种选择可能是使用ExpandoObject

        dynamic a = new ExpandoObject();
        a.Text = "Test";
        a.SubItem = new ExpandoObject();
        a.SubItem.SubText = "Blah";
        a.SubItem.SubSub = new ExpandoObject();
        a.SubItem.SubSub.Text = "Baha";

这有点丑,所以你可以保留匿名类型并使用辅助方法递归转换为ExpandoObject

    public static dynamic ConvertToExpando(object obj)
    {
        IDictionary<string, object> expando = new ExpandoObject();
            foreach(var pi in obj.GetType().GetProperties())
            {
                // there doesn't seem to be a way to know if it is an anonymous type directly. So I use IsPublic here.
                if (pi.PropertyType.IsPublic)
                {
                    expando[pi.Name] = pi.GetValue(obj);                    
                }
                else
                {
                    expando[pi.Name] = ConvertToExpando(pi.GetValue(obj));
                }
            }
        return expando;
    }

这会执行您需要的“执行巫术”。

【讨论】:

  • 应该可以写一些东西来使用反射将匿名类型递归地转换为ExpandoObject...
  • 这是我看过但害怕收到的解决方案之一。我知道这行得通,但它看起来很糟糕......
  • @SebNilsson 你很难取悦。它必须工作并且很漂亮,嗯。我添加了一个帮助方法,将匿名类型递归转换为ExpandoObject(如 Jon Skeet 所建议的那样)。
  • @JonSkeet 好主意,实际上很简单,所以我将它添加到我的答案中。
  • @mikez 我喜欢这个有创意的答案。但是IsPublic 是正确的限定符还是恰好在这种情况下起作用? :)
【解决方案3】:

使用我的开源框架 ImpromptuInterface(在 nuget 中),您可以使用 inline syntax 创建复杂的展开图。

dynamic New = Builder.New<ExpandoObject>();

dynamic dyn = New.Obj(
        Text: "Test",
        SubItem: New.Obj(
            SubText: "Bla",
            SubSub: New.Obj(
                SubSubText: "Baha"
            )
        )
    );

【讨论】:

    猜你喜欢
    • 2011-11-30
    • 1970-01-01
    • 2022-01-23
    • 1970-01-01
    • 2012-02-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多