C#DLR,使用Dynamic关键字进行数据类型推断

时间:2013-11-22 17:16:05

标签: c# .net dynamic-language-runtime

刚问:

为什么'withOffset'变量被推断为 动态 ,因为 Parse方法返回Struct

dynamic str = "22/11/2013 10:31:45 +00:01";
var withOffset = DateTimeOffset.Parse(str);

并在明确地将其回归到Struct?

之后
dynamic str = "22/11/2013 10:31:45 +00:01";
var withOffset = DateTimeOffset.Parse((string)str);

因为DateTimeOffset.Parse的返回类型是DateTimeOffset,并且编译器必须知道。记住这一点,它在运行时调用的方法,返回始终是DateTimeOffset。

规格告诉

  

由于您的方法将dynamic作为参数,因此它符合“动态绑定”

的条件
有点可疑。

有这样的规范有什么意义?或者在什么情况下DateTimeOffset.Parse不会返回STRUCT(暂时忘记DLR ......)?

如果类中的所有方法/重载都具有相同的返回类型,那么编译器需要很聪明,以便长期获得性能优势。

2 个答案:

答案 0 :(得分:4)

当您使用dynamic时,整个表达式在编译时被视为动态表达式,这会导致编译器将所有内容视为动态并获取运行时绑定。

这在C#语言规范的7.2中有解释:

  

当不涉及动态表达式时,C#默认为静态绑定,这意味着在选择过程中使用组成表达式的编译时类型。但是,当上面列出的操作中的一个组成表达式是动态表达式时,操作将被动态绑定。

这基本上意味着大多数操作(类型在规范的7.2节中列出)具有任何声明为dynamic的元素将被评估为dynamic,结果将是dynamic


这是因为在下面的行str是动态的

         dynamic str = "22/11/2013 10:31:45 +00:01";
        var withOffset = DateTimeOffset.Parse(str);

在编译时str是动态的,str的类型只在运行时才知道,这就是编译器将withOffset视为动态的原因


它已知你将str转换为datetime结构,但对于仅在运行时才能知道的编译器......

答案 1 :(得分:1)

这是一个很好的例子,说明你对返回类型的假设开始出错了。

public class Bar
{
    public string Foo(string value)
    {
        return value;
    }
}

正如您在此处所看到的,Bar显然有一个实例方法Foo,它接受​​一个字符串并返回一个字符串。

public class Baz : Bar, IDynamicMetaObjectProvider
{
    public DynamicMetaObject GetMetaObject(Expression parameter)
    {
        return new BazMetaObject(parameter, this);
    }
}

但是现在我已经创建了一个也实现了IDynamicMetaObjectProvider的派生类。这是C#用于获取DynamicMetaObject的接口,它确定动态调用在运行时的绑定方式。例如:

public class BazMetaObject : DynamicMetaObject
{
    public BazMetaObject(Expression expression, Baz value)
        : base(expression, BindingRestrictions.Empty, value)
    {
    }

    public override DynamicMetaObject BindInvokeMember(
        InvokeMemberBinder binder, DynamicMetaObject[] args)
    {
        if (binder.Name == "Foo")
        {
            return new DynamicMetaObject(
                Expression.Convert(
                    Expression.Call(
                        typeof (BazMetaObject).GetMethod("DynamicFoo")
                    ),
                    typeof (object)
                ),
                BindingRestrictions.GetTypeRestriction(
                    this.Expression,
                    this.LimitType
                )
            );
        }

        return base.BindInvokeMember(binder, args);
    }

    public static int DynamicFoo()
    {
        return 1234;
    }
}

DynamicMetaObject重载将捕获对Foo的所有调用,并动态将其重定向到DynamicFoo,其中包含完全不同的签名 - 包括返回int,而不是string {1}}。

所以,如果你要这样做......

dynamic value = "Hello, world!";

Bar bar = new Baz();
var returnValue = bar.Foo(value);

... returnValue在运行时的值为1234,而不是"Hello, world!"

现在,在现实世界中,这是疯狂邪恶。虽然可以完全重新绑定预期会以某种方式执行某些操作的功能,但做这样荒谬的事情只会让人们感到困惑。话虽如此,它在CLR中是完全可能的。

TL; DR:当您使用dynamic时,您无法始终确定您认为正确的事情。