37

I've got a function which fills an array of type sbyte[], and I need to pass this array to another function which accepts a parameter of type byte[].

Can I convert it nicely and quickly, without copying all the data or using unsafe magic?

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Vilx-
  • 104,512
  • 87
  • 279
  • 422

3 Answers3

61

Yes, you can. Since both byte and sbyte have the same binary representation there's no need to copy the data. Just do a cast to Array, then cast it to byte[] and it'll be enough.

sbyte[] signed = { -2, -1, 0, 1, 2 };
byte[] unsigned = (byte[]) (Array)signed; 
Gus
  • 25,839
  • 2
  • 51
  • 76
  • Wow, awesome! I hadn't thought of this. :P – Vilx- Mar 24 '11 at 11:32
  • 5
    Quite often i receive a mismatch array type using this approach – marcelo-ferraz Sep 02 '11 at 19:50
  • I tried this but it dint work. The results were question mark. Something that I have not seen before. Here is the code `sbyte[] txdata = { (0x00 | 0x20), ~(1 << 6) }; byte[] dss = (byte[])((Array)txdata);` – Maxwell Weru Aug 06 '12 at 12:11
  • I have no idea why is not working for you. I just tried your code and works fine. – Gus Aug 06 '12 at 19:25
  • 1
    The debugger (visualizer) seems to have problems with the 'converted' array, but it still works fine :) +1 – leppie Aug 30 '12 at 06:07
  • 12
    What this does is change the *compile-time type* of the array. Useful technique, but it depends what ends up consuming the result. For instance, System.Array.Copy(Array, int, Array, int, int) looks at the *runtime-type* of the two arrays and will throw an exception if you try to copy a "real" `byte[]` to a `sbyte[]` masquerading as a `byte[]`. – Cristian Diaconescu Jun 12 '13 at 13:51
  • `unsigned.GetValue(0)` will also return its real value `-2`. – Vladimir Reshetnikov Jan 14 '14 at 18:43
  • 2
    I get an error of "Cannot convert type 'sbyte[]' to 'byte[]'" – Kohanz Apr 25 '14 at 17:34
  • Does this alloc a new array of copy? – jayatubi Jun 05 '20 at 06:57
31

You will have to copy the data (only reference-type arrays are covariant) - but we can try to do it efficiently; Buffer.BlockCopy seems to work:

    sbyte[] signed = { -2, -1, 0, 1, 2 };
    byte[] unsigned = new byte[signed.Length];
    Buffer.BlockCopy(signed, 0, unsigned, 0, signed.Length);

If it was a reference-type, you can just cast the reference without duplicating the array:

    Foo[] arr = { new Foo(), new Foo() };
    IFoo[] iarr = (IFoo[])arr;
    Console.WriteLine(ReferenceEquals(arr, iarr)); // true
Marc Gravell
  • 1,026,079
  • 266
  • 2,566
  • 2,900
  • Edited to include note on covariance – Marc Gravell May 06 '09 at 14:51
  • 9
    @BoiseBaked I'm providing an answer by saying they need to copy the data and showing an efficient way to do that, aka "being helpful". The context of array covariance is relevant: in other virtually identical scenarios no copy is necessary. If your objection is the use of specific terms: get over it. This is a technical field with complex concepts. Using the correct terminology saves a lot of confusion. It isn't " self-gratifying", so please climb down off that high horse. – Marc Gravell Apr 28 '14 at 17:33
6

If you are using .NET 3.5+, you can use the following:

byte[] dest = Array.ConvertAll(sbyteArray, (a) => (byte)a);

Which is, I guess effectively copying all the data.

Note this function is also in .NET 2.0, but you'd have to use an anonymous method instead.

Clinton
  • 2,787
  • 1
  • 18
  • 10
  • Ugh. I'd rather use Buffer.BlockCopy() then. That at least blindly copies a bunch of bytes. This one has a function call for each of them. Performance nightmare. – Vilx- May 06 '09 at 16:23
  • 1
    Well, it all depends on how much you're copying, but yeah, The block copy makes more sense. In the end, if you want type safety, the only way is to copy. If you want to convert (re-cast) the array, the unsafe is the only way to go. – Clinton May 07 '09 at 12:21
  • LOL! Or you can do the same thing without the overhead: byte[] foo = (byte[])(Array)signed; – BoiseBaked Apr 28 '14 at 16:54
  • 2
    @BoiseBaked No, you can't. This won't change the runtime (element) type of the array. – Frank Jun 03 '15 at 09:43