将匿名类型转换为动态类型

本文关键字:动态 类型 类型转换 | 更新日期: 2023-09-27 18:17:25

我有一个函数,返回一个匿名类型,我想在我的MVC控制器中测试。

public JsonResult Foo()
{
    var data = new
                  {
                      details = "something",
                      more = "More"
                  };
    return Json(data);
}

我想验证我从Foo函数得到的数据,我现在正在做的是获得数据类型,并通过反射获得它的属性值。

[Test]
public void TestOne()
{
    var data = _controller.Foo().Data;
    var details = data.GetType().GetProperty("details").GetValue(data, null);
    var more = data.GetType().GetProperty("more").GetValue(data, null);
    Assert.AreEquals("something", details);
    Assert.AreEquals("More", more);
}

是否有类似的简单方法来检查匿名属性?

[Test]
public void TestTwo()
{
    var data = (dynamic) _controller.Foo().Data;
    var details = data.details; // RunTimeBinderException object does not contain definition for details
    var more = data.more;
    Assert.AreEquals("something", details);
    Assert.AreEquals("More", more);
}

将匿名类型转换为动态类型

匿名对象是internal,这意味着它们的成员在声明它们的程序集之外非常受限制。dynamic尊重可访问性,所以假装看不到这些成员。如果调用站点在同一个程序集中,我希望它能工作。

你的反射代码尊重成员的可访问性,但绕过了类型的可访问性-因此它工作。

这个博客有一个可行的答案:http://blog.jorgef.net/2011/06/converting-any-object-to-dynamic.html -谢谢@Jorge-Fioranelli

public static class DynamicExtensions {
    public static dynamic ToDynamic(this object value) {
        IDictionary<string, object> expando = new ExpandoObject();
        foreach (PropertyDescriptor property in TypeDescriptor.GetProperties(value.GetType()))
            expando.Add(property.Name, property.GetValue(value));
        return expando as ExpandoObject;
    }
}

匿名类型是。net中的常规静态类型,只是您不给它命名(然而,编译器会给它命名)。这就是为什么铸造它的dynamic不会工作。但是,如果您可以控制Foo(),则可以构造并返回dynamic对象而不是匿名对象,然后您的代码就可以工作了。这应该能奏效:

dynamic JsonResult Foo() {
    dynamic data = new ExpandoObject();
    data.details = "something";
    data.mode = "More";
    return Json(data);
}

由@TrueWill和@Marc Gravell建议,他们也提到了这篇博文

由于这是用于单元测试,您可以使用InternalsVisibleTo。参见匿名类型是内部的,c# 4.0动态当心!感谢@MarcGravell指出匿名对象是内部的!

底线:设置一个[assembly: InternalsVisibleTo("foo")]映射,如果你想从一个程序集共享一个匿名对象到另一个程序集。在OP的情况下,这将是在MVC控制器项目中设置它的问题,参考测试项目。在我的特殊情况下,情况正好相反(因为我将一个匿名对象从测试项目传递到"生产代码"项目中)。

在"其他项目"中能够使用它的最简单方法绝对是将其转换为dynamic,然后像正常一样使用属性。它确实可以工作,没有任何问题。

所以,底线是:我觉得Marc Gravell的答案有点不正确;这显然可以做到
(如果所讨论的项目是您可以修改的,因此您可以相应地设置InternalsVisibleTo映射,并且这不会由于任何其他原因造成问题)

您可以使用NewtonSoft或Asp.net MVC库:

var data = Json.Decode(Json.Encode(_controller.Foo().Data));

var data=JsonConvert.DeserializeObject<Dictionary<string,object>>(JsonConvert.SerializeObject((_controller.Foo().Data))