我第一次遇到了异常,使用
var
关键字咬了我。
采取这个非常简单的方法
public static Int32? GetNullableInt32(Int32 num)
{
return new Nullable<Int32>(num);
}
现在我们可以使用
dynamic
参数调用此方法,一切都会按预期工作。
public static void WorksAsAdvertised()
{
dynamic thisIsAnInt32 = 42;
//Explicitly defined type (no problems)
Int32? shouldBeNullableInt32 = GetNullableInt32(thisIsAnInt32);
Console.Write(shouldBeNullableInt32.HasValue);
}
但是,通过使用隐式类型声明
shouldBeNullableInt32
,结果与 I 的预期相去甚远。
public static void BlowsUpAtRuntime()
{
dynamic thisIsAnInt32 = 42;
//Now I'm a dynamic{int}... WTF!!!
var shouldBeNullableInt32 = GetNullableInt32(thisIsAnInt32);
//Throws a RuntimeBinderException
Console.Write(shouldBeNullableInt32.HasValue);
}
返回值被视为动态类型,而不是
Nullable<Int32>
。即使这样,底层的 Nullable<T>
也没有被保留。由于 System.Int32
没有名为 HasValue
的属性,因此会抛出 RuntimeBinderException
。
我会非常好奇地想听到有人能真正解释正在发生的事情(而不仅仅是猜测)。
shouldBeNullableInt32
的返回类型明确返回 GetNullableInt32
时,为什么 Nullable<Int32>
会被 隐式键入为动态?
Nullable<Int32>
没有被保留?为什么是dynamic{int}
? (在这里回答:C# 4:动态和可为空<>)Rick Sladkey 的答案 和Eric Lippert 的答案 同样有效。请阅读这两篇文章:)
- 当
的返回类型明确返回shouldBeNullableInt32
GetNullableInt32
时,为什么会被Nullable<Int32>
隐式键入为动态?
这是因为,虽然我们很明显
GetNullableInt32
是要被调用的方法,但由于 动态绑定,调用的实际方法被推迟到运行时,因为它正在使用动态参数进行调用。 可能存在另一个 GetNullableInt32
重载,它与 thisIsAnInt32
的运行时值更好地匹配。 该替代方法在运行时之前无法得知,可能会返回除Int32?
之外的其他类型!
因此,由于使用 动态绑定 而不是静态绑定,编译器无法在编译时假设表达式的返回类型是什么,因此表达式返回类型 dynamic。 将鼠标悬停在
var
上即可看到。
您似乎已经在这里对第二个问题得到了令人满意的解释:
Rick 的答案很好,但总而言之,您遇到了该功能的两个基本设计原则的后果:
您发现的第一个问题是第一个设计原则的结果。您要求将调用分析推迟到运行时。编译器就是这么做的。这包括将有关调用的“所有事情”推迟到运行时,包括重载解析和确定返回类型。事实上,编译器有足够的信息来猜测你的意思是无关紧要的。 如果编译器确实猜测了您的意思,那么现在您会问一个不同的问题,即“我对可用方法集做了一个微小的更改,突然编译器改变了类型的推导动态,为什么?”当编译器的行为不可预测时,用户会感到非常困惑。
(尽管如此,在少数情况下,编译器会告诉您动态代码是错误的。在某些情况下,我们知道动态绑定在运行时将
always失败,我们可以告诉您有关它们是在编译时执行的,而不是等待测试用例失败。) 您发现的第二个问题是第二个设计原则的结果。因为动态只是戴着滑稽帽子的对象,并且因为可为 null 引用或装箱的不可为 null 值类型装箱,所以不存在“动态可为 null”之类的东西。