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

只是问问而已 :

为什么’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 ..)?

编译器需要聪明,如果类中的所有方法/重载具有相同的返回类型以从长远来看获得性能优势。

当您使用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结构,但对于只在运行时才能知道的编译器……

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

 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的接口, 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

所以,如果你这样做……

 dynamic value = "Hello, world!"; Bar bar = new Baz(); var returnValue = bar.Foo(value); 

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

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

TL; DR:当你使用dynamic ,你不能总是确定你认为正确的事情。