C#DLR,使用动态关键字的数据类型推断
只是问: 为什么“withOffset”变量被推断为动态而解析方法返回StructC#DLR,使用动态关键字的数据类型推断,c#,.net,dynamic-language-runtime,C#,.net,Dynamic Language Runtime,只是问: 为什么“withOffset”变量被推断为动态而解析方法返回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); 因为Da
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..)
如果类中的所有方法/重载都具有相同的返回类型,则编译器需要更聪明,以便在长期内获得性能优势。当您使用
动态时,整个表达式在编译时被视为动态表达式,这会使编译器将所有内容都视为动态的,并获得运行时绑定
C语言规范第7.2节对此进行了解释:
当不涉及动态表达式时,C#默认为静态绑定,这意味着在选择过程中使用组成表达式的编译时类型。但是,当上面列出的操作中的一个组成表达式是动态表达式时,该操作将被动态绑定
这基本上意味着大多数操作(类型在规范第7.2节中列出)如果有任何元素被声明为动态
,将被评估为动态
,结果将是动态
这是因为在下线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);
}
}
但是现在我创建了一个派生类,它也实现了。这是C#用来获取a的接口,它决定了在运行时如何绑定动态调用。例如:
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
,而不是“你好,世界!”
现在,在现实世界中,这是疯狂的邪恶。虽然可以完全重新绑定期望以某种方式执行某些操作的函数,但这样做只会让人感到困惑。这就是说,在CLR中这是完全可能的
TL;DR:当你使用动态时,你不能总是确定你认为正确的事情。
不,我们已经知道DateTimeOffset.Parse总是返回STRUCT(DateTimeOffset),那么为什么var会推断为动态的呢?因为正如我所说的,str是动态的,var会被转换成与之相关的数据类型,我想说的是,在这种情况下,var将是动态的,因为我们事先知道返回类型是Struct,有这样粗鲁的规范有什么意义?或者DateTimeOffset.Parse是否会返回“Dynamic”?@DipakBava:由于参数是Dynamic
,编译器无法知道在运行时调用哪个方法。因此,它无法知道返回类型是什么。我们可能知道返回类型将是DateTimeOffset
,但编译器不知道,也不可能知道。@Richard,您有符合规范的点,这是否意味着如果类“Bar”中只有一个方法“public int Foo(int i)”,还有,为什么编译器需要返回动态,因为Foo没有其他重载,而且类型“Bar”是众所周知的。您可以扩展“推断为动态”吗?例如,你能添加“withOffset.foo=4;”并得到一个好的编译吗?“推断为动态”编译器认为DLR的工作就是处理这个问题……所以在你的第一个例子中,如果你添加了另一行“withOffset.foo=4;”,编译器不会标记这个,你只会在运行时得到一个错误。因为这是C语言规范所说的。第7.6.5章因为预测运行时实际绑定的方法是危险的。很抱歉,您可以使用BindInvokeMember拦截调用。然而,我觉得在继承链方面,OOP的概念是错误的。为什么基类/接口需要知道其方法/属性的派生类实现,如果基类必须知道(很少),那么方法/属性是抽象的/虚拟的,因此,方法签名保持不变。因此,在本例中,bar.Foo(value)表示意图是使用签名字符串Foo(动态值)调用Foo方法。也就是说,Baz是从Bar派生的,这意味着Bar对Baz一无所知,除非Bar将Foo()设为虚拟(但返回类型保持不变) ). 除了运行时动态解析的任何类型之外,方法的返回类型都将保持不变,除非在同一个/基类中存在具有不同返回类型的方法组