0

我在看问题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',即使在调试和悬停在 _args 变量上时,它显然确实有一个 Position 属性。

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

(解决方案已在同一个问题中显示:手动创建一个 ExpandoObject来保存参数。)

执行:

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 属性时出现的。我知道如果一开始就没有定义该属性,则必须引发异常。]

4

1 回答 1

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将使您的代码更易于阅读和编写,但这不是必需的。


关于您得到的实际异常:
请注意,它告诉您它无法Positionobject. 如果它是缺少该Position属性的匿名类型,则异常消息不会引用object而是引用匿名类型。像这样的东西:

'<>f__AnonymousType0'不包含“位置”的定义

于 2013-02-06T11:26:07.487 回答