BitConverter.GetBytes in place

2020-04-11 05:20发布

I need to get values in UInt16 and UInt64 as Byte[]. At the moment I am using BitConverter.GetBytes, but this method gives me a new array instance each time.

I would like to use a method that allow me to "copy" those values to already existing arrays, something like:

.ToBytes(UInt64 value, Byte[] array, Int32 offset);
.ToBytes(UInt16 value, Byte[] array, Int32 offset);

I have been taking a look to the .NET source code with ILSpy, but I not very sure how this code works and how can I safely modify it to fit my requirement:

public unsafe static byte[] GetBytes(long value)
{
    byte[] array = new byte[8];
    fixed (byte* ptr = array)
    {
            *(long*)ptr = value;
    }
    return array;
}

Which would be the right way of accomplishing this?

Updated: I cannot use unsafe code. It should not create new array instances.

标签: c# arrays byte
5条回答
家丑人穷心不美
2楼-- · 2020-04-11 05:35

BinaryWriter can be a good solution.

var writer = new BinaryWriter(new MemoryStream(yourbuffer, youroffset, yourbuffer.Length-youroffset));
writer.Write(someuint64);

It's useful when you need to convert a lot of data into a buffer continuously

var writer = new BinaryWriter(new MemoryStream(yourbuffer));
foreach(var value in yourints){
    writer.Write(value);
}

or when you just want to write to a file, that's the best case to use BinaryWriter.

var writer = new BinaryWriter(yourFileStream);
foreach(var value in yourints){
    writer.Write(value);
}
查看更多
不美不萌又怎样
3楼-- · 2020-04-11 05:41

You can do like this:

static unsafe void ToBytes(ulong value, byte[] array, int offset)
{
    fixed (byte* ptr = &array[offset])
        *(ulong*)ptr = value;
}

Usage:

byte[] array = new byte[9];
ToBytes(0x1122334455667788, array, 1);

You can set offset only in bytes.

If you want managed way to do it:

static void ToBytes(ulong value, byte[] array, int offset)
{
    byte[] valueBytes = BitConverter.GetBytes(value);
    Array.Copy(valueBytes, 0, array, offset, valueBytes.Length);
}

Or you can fill values by yourself:

static void ToBytes(ulong value, byte[] array, int offset)
{
    for (int i = 0; i < 8; i++)
    {
        array[offset + i] = (byte)value;
        value >>= 8;
    }
}
查看更多
对你真心纯属浪费
4楼-- · 2020-04-11 05:45

It seems that you wish to avoid, for some reason, creating any temporary new arrays. And you also want to avoid unsafe code.

You could pin the object and then copy to the array.

public static void ToBytes(ulong value, byte[] array, int offset) 
{
    GCHandle handle = GCHandle.Alloc(value, GCHandleType.Pinned);
    try
    {
        Marshal.Copy(handle.AddrOfPinnedObject(), array, offset, 8);
    }
    finally
    {
        handle.Free();
    }
}
查看更多
Animai°情兽
5楼-- · 2020-04-11 05:49

Now that .NET has added Span<T> support for better working with arrays, unmanaged memory, etc without excess allocations, they've also added System.Buffer.Binary.BinaryPrimitives.

This works as you would want, e.g. WriteUInt64BigEndian has this signature:

public static void WriteUInt64BigEndian (Span<byte> destination, ulong value);

which avoids allocating.

查看更多
我欲成王,谁敢阻挡
6楼-- · 2020-04-11 05:51

You say you want to avoid creating new arrays and you cannot use unsafe. Either use Ulugbek Umirov's answer with cached arrays (be careful with threading issues) or:

static void ToBytes(ulong value, byte[] array, int offset) {
 unchecked {
  array[offset + 0] = (byte)(value >> (8*7));
  array[offset + 1] = (byte)(value >> (8*6));
  array[offset + 2] = (byte)(value >> (8*5));
  array[offset + 3] = (byte)(value >> (8*4));
  //...
 }
}
查看更多
登录 后发表回答