将匿名类型转换为动态
Posted
技术标签:
【中文标题】将匿名类型转换为动态【英文标题】:Casting anonymous type to dynamic 【发布时间】:2012-01-17 21:42:05 【问题描述】:我有一个返回匿名类型的函数,我想在我的 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);
【问题讨论】:
由于这是用于单元测试,您可以使用InternalsVisibleTo
。见Anonymous Types are Internal, C# 4.0 Dynamic Beware! 感谢@MarcGravell 指出匿名对象是internal
!
+1 表示 InternalsVisibleTo 建议。像魅力一样工作。
【参考方案1】:
匿名对象是internal
,这意味着它们的成员在声明它们的程序集之外非常受限。 dynamic
尊重可访问性,因此假装无法看到这些成员。如果调用站点在同一个程序集中,我希望它会起作用。
您的反射代码尊重 member 可访问性,但绕过了类型的可访问性 - 因此它可以工作。
简而言之:没有。
【讨论】:
@gdoron 为什么会这样?毕竟,它仍然是一个对象。它只是没有暴露太多其他东西。 ToString()、Equals()、GetHashCode() 等仍将通过动态方式工作。它只是没有添加任何其他可见的东西。 更长的答案:也许是的,如果你可以使用InternalsVisibleTo
。在此线程中进一步查看我的答案。【参考方案2】:
这个博客有一个有效的答案: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;
【讨论】:
这对我正在做的一些编码练习很有用,但应该修改它以处理也是匿名的属性。bool isNotPrimitiveOrString = !(property.PropertyType.IsPrimitive || Equals(property.PropertyType, typeof(string))); expando.Add(property.Name, (isNotPrimitiveOrString) ? property.GetValue(value).ToDynamic() : property.GetValue(value));
另外,您似乎不能将其用作匿名对象的扩展,必须是 DynamicExtensions.ToDynamic(data)
调用/使用。
注意:上述嵌套匿名的 sn-p 似乎不适用于 PropertInfo
类,但适用于 PropertyDescriptor
(如代码示例中所示)。很有趣。【参考方案3】:
匿名类型是 .NET 中的常规静态类型,只是您没有给它命名(但是编译器会命名)。这就是为什么将其转换为 dynamic
不起作用的原因。但是,如果您可以控制Foo()
,则可以构造并返回dynamic
对象而不是匿名对象,然后您的代码就可以工作了。这应该可以解决问题:
dynamic JsonResult Foo()
dynamic data = new ExpandoObject();
data.details = "something";
data.mode = "More";
return Json(data);
【讨论】:
.Data 是“对象”。 “对象”和“动态”之间几乎没有区别,除了在消费者(这是它变得有趣的地方)。我不相信“对象”和“动态”之间的变化(然后回到动态)在这里会起到很大的作用。 @MarcGravell 我添加了代码来澄清我所说的返回动态的意思(我实际上的意思是“构造一个动态对象并返回它”,而不仅仅是“将返回类型更改为动态的”)。感谢您接受它 - 最初的编辑确实不清楚。 这项工作的主要内容是:ExpandoObject 是公共的而不是内部的(当然,IDynamicBlahBlahBlah 接口作为公共声明实现)。然而,它提示了一个重要的问题:JSON 层像 ExpandoObject 吗? (由于 IDictionary 的使用,它可能)。这也意味着我们不再使用匿名类型(问题)【参考方案4】:正如@TrueWill 和@Marc Gravell 所建议的那样,他们也提到了this blog post
由于这是用于单元测试,您可以使用 InternalsVisibleTo。请参阅匿名类型是内部的,C# 4.0 动态 当心!感谢@MarcGravell 指出匿名对象是内部的!
底线:如果您想将匿名对象从一个程序集共享到另一个程序集,请设置[assembly: InternalsVisibleTo("foo")]
映射。在 OP 的情况下,这将是在 MVC 控制器项目中设置它的问题,参考 测试项目。在我的具体情况下,反过来(因为我将一个匿名对象从我的测试项目传递到“生产代码”项目)。
“其他项目”中能够使用它的最简单方法肯定是将其转换为dynamic
,然后像正常一样使用属性。它确实有效,没有任何问题。
所以,底线:我觉得 Marc Gravell 的回答有点不正确;这显然可以做到 (如果您可以修改相关项目,因此您可以相应地设置 InternalsVisibleTo 映射,这不会因任何其他原因造成问题)。
【讨论】:
【参考方案5】:您可以使用 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))
【讨论】:
以上是关于将匿名类型转换为动态的主要内容,如果未能解决你的问题,请参考以下文章
匿名托管的 DynamicMethods 程序集:无法将类型“字符串”隐式转换为“整数”
无法将匿名类型从“System.Linq.IQueryable”转换为“System.Data.Entity.Core.Objects.ObjectQuery”