Ответ 1
В головоломке используется то, что, на мой взгляд, является ошибкой в компиляторе С#. (Ошибка также влияет на VB.NET.)
В спецификации С# 5.0 в § 4.1.1 говорится, что "Возможные значения типа bool
равны true
и false
", а в §7.11.3 говорится, что operator &(bool x, bool y)
является <сильным > логическим:
Результат
x & y
равенtrue
, если обаx
иy
равныtrue
. В противном случае результатом будетfalse
.
Очевидно, что нарушение спецификации для true & true
дает false
. Что происходит?
Во время выполнения a bool
представляется 1-байтовым целым числом. Компилятор С# использует 0 для представления false
и 1 для представления true
. Чтобы реализовать оператор &
, компилятор С# испускает команду побитовой AND
в сгенерированном ИЛ. На первый взгляд это выглядит нормально: побитовые операции AND
с участием 0 и 1 точно соответствуют логическим операциям AND
с false
и true
.
Однако в §III.1.1.2 спецификации CLI явно разрешено bool
представлять целое число, отличное от 0 или 1:
Булевой тип CLI занимает 1 байт в памяти. Битовая диаграмма всех нулей обозначает значение false. Битовая диаграмма с любым одним или несколькими установленными битами (аналогично ненулевому целому числу) обозначает значение true.
Выйдя за рамки С#, действительно возможно - и совершенно законно - создать 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
}
}
Итак, ответы на ваши вопросы:
- В настоящее время возможно для
x & y
иx && y
иметь разные значения. Однако это поведение нарушает спецификацию С#. - В настоящее время вы можете использовать
Boolean.Equals
(как показано выше), чтобы различать значенияtrue
. Однако это поведение нарушает спецификацию CLIBoolean.Equals
.