Introduction
When you're dealing with bitwise operations, the possibility to initialize variables or constants with binary literals would be helpful so that you could read the value the way it will be interpreted by your code.
Background
Initially, it was planned for C# 6.0 to introduce binary literals. Unfortunately, this feature wasn't implemented and is now scheduled for C# 7.0.
So I wrote a small helper class which allows you to do something like that, as close as possible.
Using the Code
The following class allows you to write a binary literal in the form of a string literal which will be converted into an integer of choice:
public static class BinaryLiteral
{
public static byte BinaryLiteralToByte(this string str)
{
return ToByte(str);
}
public static short BinaryLiteralToInt16(this string str)
{
return ToInt16(str);
}
public static int BinaryLiteralToInt32(this string str)
{
return ToInt32(str);
}
public static long BinaryLiteralToInt64(this string str)
{
return ToInt64(str);
}
public static byte ToByte(string str)
{
return (byte)ToInt64(str, sizeof(byte));
}
public static short ToInt16(string str)
{
return (short)ToInt64(str, sizeof(short));
}
public static int ToInt32(string str)
{
return (int)ToInt64(str, sizeof(int));
}
public static long ToInt64(string str)
{
return ToInt64(str, sizeof(long));
}
private static long ToInt64(string str, int sizeInBytes)
{
int sizeInBits = sizeInBytes * 8;
int bitIndex = 0;
long result = 0;
for (int i = str.Length - 1; i >= 0; i--)
{
char c = str[i];
if (c != ' ')
{
if (bitIndex == sizeInBits)
{
throw new OverflowException("binary literal too long: " + str);
}
if (c == '1')
{
result |= 1L << bitIndex;
}
else if (c != '0')
{
throw new InvalidCastException(String.Format("invalid character '{0}' in binary literal: {1}", c, str));
}
bitIndex++;
}
}
return result;
}
}
Then, you can use it like this:
static byte myByteBitMask = BinaryLiteral.ToByte(" 0110 1100");
static short myInt16BitMask = BinaryLiteral.ToInt16(" 1000 0000 0110 1100");
static int myInt32BitMask = BinaryLiteral.ToInt32(" 0101 1111 1000 0000 0110 1100");
static long myInt64BitMask = BinaryLiteral.ToInt64(" 0101 1111 1000 0000 0110 1100");
Or, if you prefer extension methods, like this:
static byte myByteBitMask = " 0110 1100".BinaryLiteralToByte();
static short myInt16BitMask = " 1000 0000 0110 1100".BinaryLiteralToInt16();
static int myInt32BitMask = " 0101 1111 1000 0000 0110 1100".BinaryLiteralToInt32();
static long myInt64BitMask = " 0101 1111 1000 0000 0110 1100".BinaryLiteralToInt64();
Separation of "bit-blocks" with blanks is optional and can be used at any position.
If you leave out high bits (in the above example, I skipped the 8 high bits of the Int32 and the 40 high bits of the Int64), they're treated as zeroes.
An unavoidable limitation of this solution is that you can't use it to initialize a constant identifier. So you would have to use readonly
variables instead of const
identifiers.
As the binary literal is a string constant, the variables you initialize with these methods normally should be static
to avoid repeated conversion.
Points of Interest
If you want to use underscores instead of blanks to separate "bit-blocks", just replace if (c != ' ')
with if (c != '_')
- or, to allow both: if (c != ' ' && c != '_')
.
You could achieve the same thing as this class by using only .NET-standard methods:
static int myBitMask = Convert.ToInt32("1000 0000 0110 1100".Replace(" ", ""), 2);
But it's more to type and it's less descriptive, which, I believe, is an important aspect of code.
If you favor switch
-statements over if
-statements take a look at Richard Deeming's proposed alternative way of writing the conversion code in his comment below.
History
- 22nd Mar, 2016: Added extension methods
- 24th Feb, 2016: Added hint about "static usage" and some other small improvements
- 23rd Feb, 2016: Some small improvements on readibility, conciseness and performance
- 22nd Feb, 2016: Initial version