简体   繁体   English

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

[英]Why do I have to manually create ExpandoObject to properly use the dynamic keyword?

I was looking at the question Use 'dynamic' throw a RuntimeBinderException . 我当时在看使用“动态”抛出RuntimeBinderException的问题 I face a similar problem: 我面临类似的问题:

Basically, I want to create a "HTML helper" in ASP.NET MVC that uses dynamic arguments, akin to the htmlArguments parameter for many of the existing helpers (more code below): 基本上,我想在ASP.NET MVC中创建一个使用HTML动态参数的“ HTML帮助器”,类似于许多现有帮助器的htmlArguments参数(以下更多代码):

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

View: 视图:

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

However in the naive approach, i get RuntimeBinderException thrown at me, declaring that 'object' does not contain a definition for 'Position' , even though when debugging and hovering over the _args variable, it clearly does have a Position property. 然而,天真的方法,我得到RuntimeBinderException扔给我,声称'object' does not contain a definition for 'Position' ,即使调试和悬停在_args变量时,它显然有一个位置属性。

The caller and the callee are in separate assemblies. 呼叫者和被呼叫者在单独的程序集中。 Why is that problem happening? 为什么会发生这个问题?

(The solution to that has been shown in the same question: Manually create an ExpandoObject to hold the args.) (该解决方案已在同一问题中显示: 手动创建一个ExpandoObject来容纳args。)

Implementation: 实现方式:

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());
    }
}

[Edited to make clearer that my problem arises when "obviously" the Position property is set. [为了使我的问题更清楚地说明,当“显然”设置Position属性时出现了我的问题。 I am aware that if the property never was defined in the first place, an exception must be raised.] 我知道,如果从未首先定义该属性,则必须引发异常。]

That code is fatally flawed. 该代码存在致命缺陷。

It does work, as long as you specify that property: 只要您指定该属性,它就可以工作:

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

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

That will output 0 , it will not throw a RuntimeBinderException . 这将输出0 ,不会抛出RuntimeBinderException

But the purpose of such code is the possibility for the caller to specify only the properties needed and omit the rest. 但是,此类代码的目的是使调用者可以仅指定所需的属性,而忽略其余的属性。
You are trying to check for this omission via if(args.Position != null) . 您正在尝试通过if(args.Position != null)检查此遗漏。 But that doesn't work, it already requires Position to exist. 但这不起作用,它已经需要Position存在。

When you have a look at the routing API of ASP.NET that also supports those anonymous configuration objects you will notice that the type of the parameter is object and not dynamic . 当您查看还支持那些匿名配置对象的ASP.NET路由API时,您会注意到参数的类型是object而不是dynamic
Using object instead of dynamic will enable your API to be used across assembly boundaries. 使用object而不是dynamic object将使您的API可以跨程序集边界使用。

So how does it work? 那么它是怎样工作的?

Just like in the linked answer, you need to manually create a dictionary of the properties. 就像在链接的答案中一样,您需要手动创建属性的字典。 Whether you use a plain old Dictionary<string, object> or an ExpandoObject is a matter of preference. 是否使用普通的旧Dictionary<string, object>ExpandoObject是一个优先事项。
Using ExpandoObject will make your code a bit simpler to read and write, but it is not required. 使用ExpandoObject将使您的代码更易于读写,但这不是必需的。


About the actual exception you are getting: 关于实际得到的异常:
Please note that it tells you it can't find the Position property on object . 请注意,它告诉您在object上找不到Position属性。 If it would be an anonymous type that was missing the Position property the exception message wouldn't refer to object but to an anonymous type. 如果它是缺少Position属性的匿名类型,则异常消息将不引用object而是引用匿名类型。 Something like this: 像这样:

'<>f__AnonymousType0' does not contain a definition for 'Position' '<>f__AnonymousType0'不包含'Position'的定义

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM