使用'var'和'dynamic'时出现异常

问题描述 投票:0回答:2

我第一次遇到了异常,使用

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

我会非常好奇地想听到有人能真正解释正在发生的事情(而不仅仅是猜测)。

两个问题

  1. shouldBeNullableInt32
    的返回类型明确返回 GetNullableInt32 时,为什么
    Nullable<Int32>
    会被
    隐式键入为动态
  2. 为什么底层
    Nullable<Int32>
    没有被保留?为什么是
    dynamic{int}
    在这里回答C# 4:动态和可为空<>

更新

Rick Sladkey 的答案Eric Lippert 的答案 同样有效。请阅读这两篇文章:)

c# dynamic
2个回答
20
投票
  1. shouldBeNullableInt32
    的返回类型明确返回 GetNullableInt32 时,为什么
    Nullable<Int32>
    会被
    隐式键入为动态

这是因为,虽然我们很明显

GetNullableInt32
是要被调用的方法,但由于 动态绑定调用的实际方法被推迟到运行时,因为它正在使用动态参数进行调用。 可能存在另一个
GetNullableInt32
重载,它与
thisIsAnInt32
的运行时值更好地匹配。 该替代方法在运行时之前无法得知,可能会返回除
Int32?
之外的其他类型!

因此,由于使用 动态绑定 而不是静态绑定,编译器无法在编译时假设表达式的返回类型是什么,因此表达式返回类型 dynamic。 将鼠标悬停在

var
上即可看到。

您似乎已经在这里对第二个问题得到了令人满意的解释:


18
投票

Rick 的答案很好,但总而言之,您遇到了该功能的两个基本设计原则的后果:

  1. 如果您要求动态绑定,那么您会得到动态绑定
  2. 动态只是戴着滑稽帽子的对象

您发现的第一个问题是第一个设计原则的结果。您要求将调用分析推迟到运行时。编译器就是这么做的。这包括将有关调用的“所有事情”推迟到运行时,包括重载解析和确定返回类型。事实上,编译器有足够的信息来猜测你的意思是无关紧要的。 如果编译器确实猜测了您的意思,那么现在您会问一个不同的问题,即“我对可用方法集做了一个微小的更改,突然编译器改变了类型的推导动态,为什么?”当编译器的行为不可预测时,用户会感到非常困惑。

(尽管如此,在少数情况下,编译器会告诉您动态代码是错误的。在某些情况下,我们知道动态绑定在运行时将

always

失败,我们可以告诉您有关它们是在编译时执行的,而不是等待测试用例失败。) 您发现的第二个问题是第二个设计原则的结果。因为动态只是戴着滑稽帽子的对象,并且因为可为 null 引用或装箱的不可为 null 值类型装箱,所以不存在“动态可为 null”之类的东西。

© www.soinside.com 2019 - 2024. All rights reserved.