Disclaimer: It's not about Nullable<bool>
or something.
I've seen someone created a bool value which was neither true
or false
. It was an example why exhaustive match is not possible in C#. Unfortunately, I've lost source code with an example, which I'd really like to restore.
So the question is: how to make default
arm to be called in the following code?
bool b = GetMyWeirdBool()
switch (b)
{
case true: Console.WriteLine("true"); break;
case false: Console.WriteLine("false"); break;
default: Console.WriteLine("neither o_0"); break;
}
IIRC there was some dirty hacking via Marshal
or StructLayout
or something.
Probably a true with a value of 43 or similar. The point is: Boolean
is a lie - it doesn't exist at the IL level. It is simply an integer with a value that is zero or non-zero. That works fine for the "brtrue" and "brfalse" opcodes for any boolean values (1,-1 or 42) - the problem comes when you do an equality test between two values that are meant to be booleans: a "ceq" opcode will fail for two true booleans that aren't actually equal. Likewise, a "switch" opcode (jump-table - commonly used for switch
in C#) will fail if the values aren't what the compiler expected.
This may confuse some APIs, returning false on equality to another true value, but reporting true when compared via brtrue.
Via unsafe
code treating an int
as a bool
:
static unsafe bool GetMyWeirdBool()
{
int i = 42;
return *(bool*)(&i);
}
via IL:
static bool GetMyWeirdBool()
{
var method = new DynamicMethod("evil", typeof(bool), null);
var il = method.GetILGenerator();
il.Emit(OpCodes.Ldc_I4, 42);
il.Emit(OpCodes.Ret);
var func = (Func<bool>)method.CreateDelegate(typeof(Func<bool>));
return func();
}
via a struct
with overlapped fields:
[StructLayout(LayoutKind.Explicit)]
struct Evil
{
public Evil(int value)
{
Boolean = false;
Int32 = value;
}
[FieldOffset(0)]
public bool Boolean;
[FieldOffset(0)]
public int Int32;
}
static bool GetMyWeirdBool()
{
var val = new Evil(42);
return val.Boolean;
}
You could use a struct with explicit layout:
[StructLayout(LayoutKind.Explicit)]
public struct NotBool
{
[FieldOffset(0)]
public readonly bool Value;
[FieldOffset(0)]
private readonly int Int;
public NotBool(int intValue)
{
Value = false;
Int = intValue;
}
}
Test:
NotBool notBool = new NotBool(255);
switch (notBool.Value)
{
case true:
break;
case false:
break;
default:
Debug.WriteLine("Default");
break;
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With