我阅读了关于条件逻辑运算符 ||&&(也称为短路逻辑运算符)的 C# 语言规范。对我来说,似乎不清楚这些是否存在于可为空的 bool 值,即操作数类型 Nullable<bool> (也写为 bool? ),所以我尝试使用非动态类型:

bool a = true;
bool? b = null;
bool? xxxx = b || a;  // compile-time error, || can't be applied to these types

这似乎解决了问题(我无法清楚地理解规范,但假设 Visual C# 编译器的实现是正确的,现在我知道了)。

但是,我也想尝试使用 dynamic 绑定(bind)。所以我尝试了这个:
static class Program
{
  static dynamic A
  {
    get
    {
      Console.WriteLine("'A' evaluated");
      return true;
    }
  }
  static dynamic B
  {
    get
    {
      Console.WriteLine("'B' evaluated");
      return null;
    }
  }

  static void Main()
  {
    dynamic x = A | B;
    Console.WriteLine((object)x);
    dynamic y = A & B;
    Console.WriteLine((object)y);

    dynamic xx = A || B;
    Console.WriteLine((object)xx);
    dynamic yy = A && B;
    Console.WriteLine((object)yy);
  }
}

令人惊讶的结果是,这无一异常(exception)地运行。

好吧,xy 并不奇怪,它们的声明导致两个属性都被检索,结果值如预期的那样,x 是 0x251812231343241 和 0x2518124313432123131313131313131313231313231431323143132314313231431313131313131313131323132313313134312313都都被检索到了。

但是对 truey 的评估导致没有绑定(bind)时间异常,并且只读取了属性 null ,而不是 xx 。为什么会发生这种情况?如您所知,我们可以更改 A || B getter 以返回一个疯狂的对象,例如 A ,并且 B 仍然会评估为 B 而不绑定(bind)...

评估 "Hello world" (对于 xx )也不会导致绑定(bind)时间错误。当然,这里检索了这两个属性。为什么运行时绑定(bind)器允许这样做?如果从 true 返回的对象更改为“坏”对象(如 A && B ),则会发生绑定(bind)异常。

这是正确的行为吗? (你怎么能从规范中推断出来?)

如果您尝试yy作为第一个操作数,无论Bstring给予运行时绑定(bind)异常(BB || A工作细如一切正常与非短路运营商B && AB | A)。

(尝试使用 Visual Studio 2013 的 C# 编译器和运行时版本 .NET 4.5.2。)

最佳答案

首先,感谢您指出规范在非动态 nullable-bool 情况下不清楚。我会在以后的版本中修复它。编译器的行为是预期的行为; &&|| 不应该在可为空的 bool 值上工作。

不过,动态绑定(bind)器似乎没有实现此限制。相反,它单独绑定(bind)组件操作: &/|?: 。因此,如果第一个操作数恰好是 truefalse(它们是 bool 值,因此允许作为 ?: 的第一个操作数),则它能够混​​淆,但是如果你给出 null 的第一个例子,例如 0x21812314314141431451321435314314143143141414314141414135143513513510上面),你会得到一个运行时绑定(bind)异常。

如果你仔细想想,你就会明白为什么我们以这种方式实现动态 B && A&&,而不是作为一个大的动态操作:动态操作在它们的操作数被评估后在运行时绑定(bind),这样绑定(bind)就可以基于运行时类型这些评估的结果。但是这种急切的评估违背了短路运算符的目的!因此,动态 ||&& 的生成代码将评估分解为多个部分,并将按如下方式进行:

  • 评估左操作数(我们称结果为 || )
  • 尝试通过隐式转换将其转换为 x,或 booltrue 运算符(如果不能,则失败) 0x21239
  • 使用 false 作为 x 操作中的条件
  • 在真分支中,使用 ?: 作为结果
  • 在 false 分支中,现在评估第二个操作数(让我们调用结果 x )
  • 尝试绑定(bind)y&操作符根据|和0x2518123134313431300x251812314313313431300x251812313313131313131313313313131313313313133133131313131313131313131313313313313313131331331313131313313131313131313131313131313131313个的运行时类型的运行时的运行时类型的操作符
  • 应用选定的运算符

  • 这是允许通过操作数的某些“非法”组合的行为:x 运算符成功地将第一个操作数视为不可为空的 bool 值,y?: 运算符从未成功地将其视为可空值的检查,并且两个 bool 值的检查从未成功他们同意。

    所以它不是动态的 && 和 ||处理可空值。只是与静态情况相比,它们的实现方式有点过于宽松了。这可能应该被视为一个错误,但我们永远不会修复它,因为这将是一个破坏性的变化。此外,它几乎不会帮助任何人收紧行为。

    希望这能解释发生了什么以及为什么!这是一个有趣的领域,当我们实现动态时,我经常发现自己对我们做出的决定的后果感到困惑。这个问题很好吃 - 谢谢你提出来!

    疯子

    关于c# - 做短路运算符 ||和 && 存在于可为空的 bool 值吗? RuntimeBinder 有时会这么认为,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/27508991/

    10-13 07:32
    查看更多