为什么我必须手动创建ExpandoObject才能正确使用dynamic关键字
本文关键字:关键字 dynamic ExpandoObject 创建 为什么 | 更新日期: 2023-09-27 17:57:50
我正在研究使用"dynamic"抛出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'
,即使在调试和悬停在_args变量上时,它显然有一个Position属性。
调用者和被调用者位于不同的程序集中。为什么会出现这种问题?
(解决方案已经在同一个问题中显示:手动创建一个ExpandoObject来保存args。)
实施:
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());
}
}
[编辑以更清楚地表明,当"显然"设置了Position属性时,我的问题就会出现。我知道,如果该属性从未在一开始就定义过,则必须引发异常。]
该代码存在致命缺陷。
只要您指定了属性:,它就可以工作
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
将使代码的读写更加简单,但这不是必需的。
关于您得到的实际异常:
请注意,它告诉您在object
上找不到Position
属性。如果是缺少Position
属性的匿名类型,则异常消息不会引用object
,而是引用匿名类型。类似这样的东西:
'<>f__AnonymousType0'
不包含"位置"的定义