当x和y都为真时,"x&y"怎么会变错?

mal*_*rdz 20 c# boolean pex bitwise-operators logical-operators

语境:

我正在学习C#并且一直在搞乱Pex for fun网站.该站点向您提出重新实施秘密算法的挑战,方法是在站点中键入代码并检查输入和输出在您的实现和秘密实现之间的差异.

问题:

无论如何,我被困在一个名为XAndY的基本代码决斗上.

从名称来看,答案显而易见:

public static bool Puzzle(bool x, bool y) 
{
    return x && y;
}
Run Code Online (Sandbox Code Playgroud)

但是,这是不正确的,Pex告诉我,以下输入产生的结果与秘密实现不同:

输入:

x:true y:true(0x02)

输出:

我的实现:true(0x02)

秘密实施:假

不匹配你的拼图方法产生了错误的结果.

代码:Puzzle(true,PexSafeHelpers.ByteToBoolean((byte)2));

在尝试比较不同类型的true之后出现了很多困惑之后,我意识到Pex正在寻找的实现实际上只是使用了按位AND:

return x & y;
Run Code Online (Sandbox Code Playgroud)

问题:

我认为,出于语义和短路的原因,您应该使用逻辑&&来比较布尔值,但无论如何:

  1. 这是否意味着x & y并且对于所有可能的bool参数x && y最终没有相同的输出?(或者它可能是Pex中的一些东西吗?)
  2. 这是否意味着您可以区分trueC#中不同的bool值?如果是这样,怎么样?

Mic*_*Liu 20

这个难题正在利用我认为是C#编译器中的错误.(该bug也会影响VB.NET.)

在C#5.0规范中,§4.1.8说"类型的可能值booltruefalse",而§7.11.3说这operator &(bool x, bool y)是一个逻辑运算符:

结果x & ytrue,如果这两个xytrue.否则,结果是false.

这显然违反了true & true收益规范false.这是怎么回事?

在运行时,a bool由1字节整数表示.C#编译器使用0表示false,1表示true.为了实现&运算符,C#编译器在生成的IL中发出按位 AND指令.乍一看,这似乎没问题:AND涉及0和1的按位运算完全对应于AND涉及false和的逻辑运算true.

但是,CLI规范的 §III.1.1.2 明确允许用bool0或1以外的整数表示:

CLI布尔类型在内存中占用1个字节.全零的位模式表示值false.设置任何一个或多个位的位模式(类似于非零整数)表示值true.

超越C#的范围,确实可能 - 并且完全合法 - 创建一个bool值,例如2,从而导致&意外行为.这就是Pex网站正在做的事情.

这是一个演示:

using System;
using System.Reflection.Emit;

class Program
{
    static void Main()
    {
        DynamicMethod method =
            new DynamicMethod("ByteToBoolean", typeof(bool), new[] { typeof(byte) });
        ILGenerator il = method.GetILGenerator();
        il.Emit(OpCodes.Ldarg_0); // Load the byte argument...
        il.Emit(OpCodes.Ret);     // and "cast" it directly to bool.
        var byteToBoolean =
            (Func<byte, bool>)method.CreateDelegate(typeof(Func<byte, bool>));

        bool x = true;
        bool y = byteToBoolean(2);
        Console.WriteLine(x);               // True
        Console.WriteLine(y);               // True
        Console.WriteLine(x && y);          // True
        Console.WriteLine(x & y);           // False (!) because 1 & 2 == 0
        Console.WriteLine(y.Equals(false)); // False
        Console.WriteLine(y.Equals(true));  // False (!) because 2 != 1
    }
}
Run Code Online (Sandbox Code Playgroud)

所以你的问题的答案是:

  1. 目前,它的可能x & y,并x && y有不同的值.但是,此行为违反了C#规范.
  2. 目前,您可以使用Boolean.Equals(如上所示)区true分值.但是,此行为违反了CLI规范Boolean.Equals.

  • @JackPRead:有趣。在我写下这个答案后的某个时候,C# 编译器被更改为使用 `&amp;` 而不是条件跳转来为简单的布尔变量实现 `&amp;&amp;`,所以现在 `x &amp; y` 和 `x &amp;&amp; y` 都错误地计算为错误的。:-( (2认同)