我在看问题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属性。我知道如果首先从未定义属性,则必须引发异常。]
答案 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'
不包含“位置”