为什么我必须手动创建ExpandoObject才能正确使用dynamic关键字?

时间:2013-02-06 11:20:56

标签: c# dynamic expandoobject

我在看问题Use 'dynamic' throw a RuntimeBinderException。我面临类似的问题:

基本上,我想在ASP.NET MVC中创建一个“HTML帮助器”,它使用动态参数,类似于许多现有助手的htmlArguments参数(下面有更多代码):

public BootstrapCell(Action<string> emitContentAction, dynamic args)

查看:

@using (grid.Cell(ViewContext.Writer.Write, new {Position = 4}))
{
     <p>zomg!</p>
}

然而,在天真的方法中,我向RuntimeBinderException发出'object' does not contain a definition for 'Position'声明public class Cell { private readonly string _tagName; private dynamic _args; private Action<string> EmitContentAction; public BootstrapCell(Action<string> emitContentAction, dynamic args) : DisposableBaseClass { _args = args; EmitContentAction = emitContentAction; OnContextEnter(); } protected void OnContextEnter() { var sb = new StringBuilder("<"); sb.Append(_tagName); if (_args.Position > 0) { sb.Append(" class=\"offset"); sb.Append(args.Position); sb.Append("\""); } sb.Append(">"); EmitContentAction(sb.ToString()); } } ,即使调试并将鼠标悬停在_args变量上时,显然确实位置属性。

调用者和被调用者位于不同的程序集中。为什么会出现这个问题?

(对此问题的解决方案已在同一问题中显示:Manually create an ExpandoObject来保存args。)

实现:

{{1}}

[编辑更明确我的问题出现时“明显”设置了Position属性。我知道如果首先从未定义属性,则必须引发异常。]

1 个答案:

答案 0 :(得分:3)

该代码存在致命缺陷。

只要您指定该属性,

就可以工作:

void Bar()
{
    Foo(new {Position = 0});
}

void Foo(dynamic args)
{
    Console.WriteLine(args.Position);
}

这将输出0,它不会抛出RuntimeBinderException

但是这样的代码的目的是让调用者只能指定所需的属性并省略其余的属性 您正试图通过if(args.Position != null)检查此遗漏。但这不起作用,它已经需要Position存在。

当您查看也支持这些匿名配置对象的ASP.NET的路由API时,您会注意到参数的类型是object而不是dynamic
使用object代替dynamic将使您的API能够跨程序集边界使用。

那它是如何运作的?

就像在链接的答案中一样,您需要手动创建属性的字典。是否使用普通的Dictionary<string, object>ExpandoObject是一个偏好问题 使用ExpandoObject将使您的代码更易于读写,但这不是必需的。


关于您获得的实际例外情况:
请注意,它告诉您在Position上找不到object属性。如果它是缺少Position属性的匿名类型,则异常消息不会引用object而是引用匿名类型。像这样:

  

'<>f__AnonymousType0'不包含“位置”

的定义