# Converting hex/bytes/int drama got a happy end

You won't believe how happy I have been,  when I noticed the new [BitConverter](https://docs.microsoft.com/en-us/dotnet/api/system.bitconverter?f1url=%3FappId%3DDev16IDEF1%26l%3DEN-US%26k%3Dk(System.BitConverter)%3Bk(DevLang-csharp)%26rd%3Dtrue&view=net-6.0) class in .Net. Finally, stop taking some stupid helper class around with you, just for converting byte to hex or other types and vice versa for example for debug logging. Now mostly a simple one-liner!

### We take a very short look into BitConverter

#### byte to hex dump 
[BitConverter.ToString](https://docs.microsoft.com/en-us/dotnet/api/system.bitconverter.tostring?f1url=%3FappId%3DDev16IDEF1%26l%3DEN-US%26k%3Dk(System.BitConverter.ToString)%3Bk(DevLang-csharp)%26rd%3Dtrue&view=net-6.0)
```
// 104 is the int representation of the char 'h', hex '68'
// 105 is the int representation of the char 'i', hex '69'
byte[] someByteArr = new byte[2] {104,105}; 

string hexDump = BitConverter.ToString(someByteArr);
Console.WriteLine(hexDump);
// output: 68-69
```
There are numerous [hexadecimal](https://en.wikipedia.org/wiki/Hexadecimal) notations. For example: 68-69, 0x68 0x69, \x68 \x69 and so on...
If Hexadecimal is unknown to you, I strongly recommend reading the article from Wikipedia as it will improve your understanding massively. 

#### bytes to int32 (exemplary)
Simply:
```
byte[] someByteArr = new byte[2] {132,1};

short result = BitConverter.ToInt16(someByteArr);
Console.WriteLine(result);
// output: 388
```
When converting this way, please remind the byte required byte array length (e.g., 2 bytes for int16/4bytes for int32/8bytes for int64) and the order of bytes you received (host byte order or network byte order / lowHIGH or HIGHlow). 

Some little history: In general, it is called 'endianness', the order of the bytes of a word in the memory, where we have big-endian and little-endian. In the beginning the mainframes dominated the world which all used big-endianness. When the personal computer came up it switched to little-endianness. However, the network is still mostly big-endianness because the IETF RFCs (public declarations of protocols) use them. You can read more about this at [Wikipedia](https://en.wikipedia.org/wiki/Endianness).
You can test endianness yourself if you simply switch the bytes in the upper array:
```
byte[] someByteArr = new byte[2] {1,132};

short result = BitConverter.ToInt16(someByteArr);
Console.WriteLine(result);
// output: -31743
```
The framework itself does implement [IPAddress.HostToNetworkOrder](https://docs.microsoft.com/en-us/dotnet/api/system.net.ipaddress.hosttonetworkorder?view=net-6.0) and [IPAddress.NetworkToHostOrder](https://docs.microsoft.com/en-us/dotnet/api/system.net.ipaddress.networktohostorder?view=net-6.0) for switching between the endiannesses. As an alternative, just use [Array.Reverse](https://docs.microsoft.com/en-us/dotnet/api/system.array.reverse?view=net-6.0).

#### Converting one byte to int representation
The conversion is still using the good old [Convert](https://docs.microsoft.com/en-us/dotnet/api/system.convert?f1url=%3FappId%3DDev16IDEF1%26l%3DEN-US%26k%3Dk(System.Convert)%3Bk(DevLang-csharp)%26rd%3Dtrue&view=net-6.0) class. Where we simply can convert one byte to the corresponding representation. This is a hard conversion, so exceptions may and often happen. We are just converting the representation, not dealing with the real 'data'. 
For example, a byte can be also expressed using an int as used in above examples for the byte array initialization:
```
byte[] someByteArr = new byte[2] {132,1};

int result = Convert.ToInt32(someByteArr[0]);
Console.WriteLine(result);
// output: 132

result = Convert.ToInt32(someByteArr[1]);
Console.WriteLine(result);
// output: 1
```

### Just taking an exit before the end to go the way through unsigned/signed
Here we will use integer / int here. uint stands for unsigned integer whereas int is for signed integer. Unsigned integers can only have positive values and signed also negative. 
In the memory itself however, the occupied space is the same so a 'trick' is used to accomplish this resulting in different ranges:
int	-2,147,483,648 to 2,147,483,647
uint	0 to 4,294,967,295
You can read more about this [here](https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/builtin-types/integral-numeric-types).

As the data in the memory is different, the type is important for casting. If the wrong type is used an 'overflow' may happen which does result in wrong interpretation of the data.

I will not dive deep into the memory storage itself, there are much better articles about that for example at Wikipedia, which you already find referenced in this article.

### Credits
Photo by [mostafa meraji](https://unsplash.com/@mostafa_meraji?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText) on [Unsplash](https://unsplash.com/s/photos/drama?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText)
  
