将匿名类型转换为动态

gdo*_*ica 41 .net c# asp.net-mvc unit-testing anonymous-types

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

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

我想验证从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);
}
Run Code Online (Sandbox Code Playgroud)

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

[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);
}
Run Code Online (Sandbox Code Playgroud)

Mar*_*ell 39

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

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

简而言之:没有.

  • @gdoron为什么会这样?毕竟,它仍然是一个对象.它只是没有暴露太多其他东西.ToString(),Equals(),GetHashCode()等仍将通过动态工作.它只是不添加任何其他可见的东西. (2认同)
  • 更长的答案:_也许_是的,如果您可以使用`InternalsVisibleTo`。在此线程中进一步查看我的答案。 (2认同)

Haf*_*hor 25

这个博客有一个可靠的答案: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;
    }
}
Run Code Online (Sandbox Code Playgroud)


das*_*ght 9

匿名类型是.NET中的常规静态类型,只是你不给它一个名字(编译器,但是,它).这就是为什么把它投射dynamic不起作用的原因.但是,如果您可以控制Foo(),则可以构造并返回一个dynamic对象而不是匿名对象,然后您的代码将起作用.这应该做的伎俩:

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

  • 完成这项工作的主要因素是:ExpandoObject是公共的而不是内部的(当然,作为公共声明实现IDynamicBlahBlahBlah接口)。但是,它提示了一个重要的问题:JSON层是否像ExpandoObject一样?(由于IDictionary的用法,*可能*)。这也意味着我们不再使用匿名类型(问题) (2认同)

Per*_*erg 7

正如@TrueWill和@Marc Gravell所建议的那样,他也提到了这篇博文

由于这是用于单元测试,因此可以使用InternalsVisibleTo.请参阅匿名类型是内部的,C#4.0动态小心!感谢@MarcGravell指出匿名对象是内部的!

底线:[assembly: InternalsVisibleTo("foo")]如果要将匿名对象从一个程序集共享到另一个程序集,请设置映射.在OP情况下,将在MVC控制器项目中设置此项,参考测试项目.在我的具体情况下,反过来(因为我将一个匿名对象从我的测试项目传递到"生产代码"项目).

在"其他项目"中能够使用它的最简单方法是将其强制转换为dynamic然后只使用正常的属性.它确实有效,没有任何问题.

所以,底线:我觉得Marc Gravell的回答有些不正确; 这显然可以做到
(如果有问题的项目可以由你修改,那么你可以相应地设置InternalsVisibleTo映射,这不会因为其他原因而造成问题).