83

I thought .net had some kind of easy conversion method to use for converting an int into a byte array? I did a quick search and all solutions are bit masking/shifting one byte at a time, like "the good ol days". Is there not a ToByteArray() method somewhere?

Brady Moritz
  • 8,624
  • 8
  • 66
  • 100

6 Answers6

141

Update for 2020 - BinaryPrimitives should now be preferred over BitConverter. It provides endian-specific APIs, and is less allocatey.


byte[] bytes = BitConverter.GetBytes(i);

although note also that you might want to check BitConverter.IsLittleEndian to see which way around that is going to appear!

Note that if you are doing this repeatedly you might want to avoid all those short-lived array allocations by writing it yourself via either shift operations (>> / <<), or by using unsafe code. Shift operations also have the advantage that they aren't affected by your platform's endianness; you always get the bytes in the order you expect them.

Marc Gravell
  • 1,026,079
  • 266
  • 2,566
  • 2,900
  • 2
    +1: For clarity, I would replace in "[...] in the order you expect them" with "big-endian". – Ani Nov 14 '10 at 13:52
  • Why does `BitConverter.GetBytes(250)` return `[250,0,0,0]`, and not `[250]`? `Byte.MaxValue == 255`, right? –  May 14 '17 at 23:20
  • @Tobi because you're calling GetBytes(int) - so: 4 bytes, and your CPU is little-endian. There would be no purpose in a GetBytes (byte) method. – Marc Gravell May 14 '17 at 23:42
  • 1
    @Tobi re GetBytes(256): you don't get 255,1 because it is explicitly the fixed width representation - bytes. The little-endian (your CPU) fixed width 32 bit representation of 256 is what you'll get – Marc Gravell May 14 '17 at 23:52
  • hi Marc, which approach will provider even slightly better performance, `bitwise` or `unsafe`? – Timeless Nov 24 '17 at 08:55
  • 1
    @Timeless *usually*: `unsafe` - but it varies depending on *exactly* what you're doing - the best thing to do would be to implement it twice and test 10-million or so of both, timing them – Marc Gravell Nov 24 '17 at 10:41
  • How does one inverse the order of the array if it is little Endian? I want it to be in the order `00-00-00-01` not `01-00-00-00` – Zapnologica Feb 02 '20 at 10:55
  • @Zapnologica for that I would recommend `BinaryPrimitives` - it has endian-specific encode and decode operations. It didn't exist when I posted this answer, but does now. https://learn.microsoft.com/en-us/dotnet/api/system.buffers.binary.binaryprimitives?view=netcore-3.1 – Marc Gravell Feb 02 '20 at 13:32
40

Marc's answer is of course the right answer. But since he mentioned the shift operators and unsafe code as an alternative. I would like to share a less common alternative. Using a struct with Explicit layout. This is similar in principal to a C/C++ union.

Here is an example of a struct that can be used to get to the component bytes of the Int32 data type and the nice thing is that it is two way, you can manipulate the byte values and see the effect on the Int.

  using System.Runtime.InteropServices;

  [StructLayout(LayoutKind.Explicit)]
  struct Int32Converter
  {
    [FieldOffset(0)] public int Value;
    [FieldOffset(0)] public byte Byte1;
    [FieldOffset(1)] public byte Byte2;
    [FieldOffset(2)] public byte Byte3;
    [FieldOffset(3)] public byte Byte4;

    public Int32Converter(int value)
    {
      Byte1 = Byte2 = Byte3 = Byte4 = 0;
      Value = value;
    }

    public static implicit operator Int32(Int32Converter value)
    {
      return value.Value;
    }

    public static implicit operator Int32Converter(int value)
    {
      return new Int32Converter(value);
    }
  }

The above can now be used as follows

 Int32Converter i32 = 256;
 Console.WriteLine(i32.Byte1);
 Console.WriteLine(i32.Byte2);
 Console.WriteLine(i32.Byte3);
 Console.WriteLine(i32.Byte4);

 i32.Byte2 = 2;
 Console.WriteLine(i32.Value);

Of course the immutability police may not be excited about the last possiblity :)

Chris Taylor
  • 52,623
  • 10
  • 78
  • 89
  • +1: This is a nice approach. If you're worried about the immutability police, you could make the fields private and expose them with get-only properties. – Ani Nov 14 '10 at 13:50
  • @Ani, Thanks, that is a good idea to make the `byte` members private and create get-only properties. – Chris Taylor Nov 14 '10 at 15:30
  • The statement(s) 'Byte1 = Byte2 = Byte3 = Byte4 = 0;' are redundant as the subsequent Value assignment overwrites them. This is precisely the kind of aliasing that drives compiler optimiser writers nuts. The union is elegant as it collapses the multiple writes to a single one through the memory channel. Using the struct also avoids the array allocation for extremely frequent cases. – Pekka Jun 11 '13 at 19:50
  • @Pekka, the compiler enforces that you initialize all members of the struct and it does not take into account the fact that the bytes are overlaid by the int which is why the initialization is required, redundant but required. – Chris Taylor Jun 21 '13 at 04:39
  • @Chris, true, precisely the optimiser police fighting with the aliases. – Pekka Jun 28 '13 at 10:36
  • Isn't this solution susceptible to the big/little endian difficulty, too? If you are on a big endian system, the content of the "Value" field will be reversed, right? – eikuh Apr 13 '16 at 07:10
  • @eikuh, absolutely, Bitconverter.IsLittleEndian can help with determining the correct order to read the bytes. BUT, like I said this is no the right answer, I just shared it because at the time the technique was not widely seen. – Chris Taylor Apr 14 '16 at 03:34
  • @ChrisTaylor, where did you say it's not the right answer? – eikuh Apr 15 '16 at 09:34
  • @eikuh, the first sentence "Marc's answer is of course the right answer." and the rest of that paragraph explains why I shared this. Now, that is not to say that it is not the right answer for another problem, but for the original question I think Marc's answer is correct. I hope that helps. – Chris Taylor Apr 15 '16 at 20:38
  • By using this answer you can remove the redundant statement "byte1 = byte2 = byte3 = byte4 = 0" and also speed up the code for one or two nanoseconds https://stackoverflow.com/questions/9174787/struct-layout-explicit-constructor-fully-assign-fields I also implemented this, that may be useful for those who thing this method is a good idea: https://gist.github.com/forestrf/e421166fc32ef707fe3817360d711a61 – Forestrf Jan 06 '18 at 23:08
2

This may be OT but if you are serializing a lot of primitive types or POD structures, Google Protocol Buffers for .Net might be useful to you. This addresses the endianness issue @Marc raised above, among other useful features.

Steve Townsend
  • 53,498
  • 9
  • 91
  • 140
2

If you came here from Google

Alternative answer to an older question refers to John Skeet's Library that has tools for letting you write primitive data types directly into a byte[] with an Index offset. Far better than BitConverter if you need performance.

Older thread discussing this issue here

John Skeet's Libraries are here

Just download the source and look at the MiscUtil.Conversion namespace. EndianBitConverter.cs handles everything for you.

JamesHoux
  • 2,999
  • 3
  • 32
  • 50
1

Heap allocation free using MemoryMarshal and Span

int messageLen = 1111;
Span<byte> messageTypeBytes = MemoryMarshal.AsBytes(MemoryMarshal.CreateSpan(ref messageLen, 1));

BenchmarkDotnet

|            Method |      Mean |     Error |    StdDev | Allocated |
|------------------ |----------:|----------:|----------:|----------:|
| MemoryMarshalTest | 0.0023 ns | 0.0017 ns | 0.0014 ns |         - |
CorrM
  • 498
  • 6
  • 18
0

Most of the answers here are either 'UnSafe" or not LittleEndian safe. BitConverter is not LittleEndian safe. So building on an example in here (see the post by PZahra) I made a LittleEndian safe version simply by reading the byte array in reverse when BitConverter.IsLittleEndian == true

void Main(){    
    Console.WriteLine(BitConverter.IsLittleEndian); 
    byte[] bytes = BitConverter.GetBytes(0xdcbaabcdfffe1608);
    //Console.WriteLine(bytes); 
    string hexStr = ByteArrayToHex(bytes);
    Console.WriteLine(hexStr);
}

public static string ByteArrayToHex(byte[] data) 
{ 
   char[] c = new char[data.Length * 2]; 
   byte b; 
  if(BitConverter.IsLittleEndian)
  {
        //read the byte array in reverse
        for (int y = data.Length -1, x = 0; y >= 0; --y, ++x) 
        { 
            b = ((byte)(data[y] >> 4)); 
            c[x] = (char)(b > 9 ? b + 0x37 : b + 0x30); 
            b = ((byte)(data[y] & 0xF)); 
            c[++x] = (char)(b > 9 ? b + 0x37 : b + 0x30); 
        }               
    }
    else
    {
        for (int y = 0, x = 0; y < data.Length; ++y, ++x) 
        { 
            b = ((byte)(data[y] >> 4)); 
            c[x] = (char)(b > 9 ? b + 0x37 : b + 0x30); 
            b = ((byte)(data[y] & 0xF)); 
            c[++x] = (char)(b > 9 ? b + 0x37 : b + 0x30); 
        }
    }
    return String.Concat("0x",new string(c));
}

It returns this:

True
0xDCBAABCDFFFE1608

which is the exact hex that went into the byte array.

GregJF
  • 456
  • 4
  • 14
  • It doesn't start from an int, though. Not to mention, it's a lot more logical to do that endianness check on _read_ so you're sure what your data is like in the array. – Nyerguds Oct 10 '16 at 08:34