Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

reinterpret_cast in C#

Tags:

arrays

c#

casting

I'm looking for a way to reinterpret an array of type byte[] as a different type, say short[]. In C++ this would be achieved by a simple cast but in C# I haven't found a way to achieve this without resorting to duplicating the entire buffer.

Any ideas?

like image 973
Andreas Avatar asked Jan 26 '09 12:01

Andreas


2 Answers

You can achieve this but this is a relatively bad idea. Raw memory access like this is not type-safe and can only be done under a full trust security environment. You should never do this in a properly designed managed application. If your data is masquerading under two different forms, perhaps you actually have two separate data sets?

In any case, here is a quick and simple code snippet to accomplish what you asked:

byte[] bytes = new byte[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
int byteCount = bytes.Length;

unsafe
{
    // By using the fixed keyword, we fix the array in a static memory location.
    // Otherwise, the garbage collector might move it while we are still using it!
    fixed (byte* bytePointer = bytes)
    {
        short* shortPointer = (short*)bytePointer;

        for (int index = 0; index < byteCount / 2; index++)
        {
            Console.WriteLine("Short {0}: {1}", index, shortPointer[index]);
        }
    }
}
like image 154
Sander Avatar answered Nov 15 '22 20:11

Sander


There are four good answers to this question. Each has different downsides. Of course, beware of endianness and realize that all of these answers are holes in the type system, just not particularly treacherous holes. In short, don't do this a lot, and only when you really need to.

  1. Sander's answer. Use unsafe code to reinterpret pointers. This is the fastest solution, but it uses unsafe code. Not always an option.

  2. Leonidas' answer. Use StructLayout and FieldOffset(0) to turn a struct into a union. The downsides to this are that some (rare) environments don't support StructLayout (eg Flash builds in Unity3D) and that StructLayout cannot be used with generics.

  3. ljs' answer. Use BitConverter methods. This has the disadvantage that most of the methods allocate memory, which isn't great in low-level code. Also, there isn't a full suite of these methods, so you can't really use it generically.

  4. Buffer.BlockCopy two arrays of different types. The only downside is that you need two buffers, which is perfect when converting arrays, but a pain when casting a single value. Just beware that length is specified in bytes, not elements. Buffer.ByteLength helps. Also, it only works on primitives, like ints, floats and bools, not structs or enums.

But you can do some neat stuff with it.

public static class Cast {

    private static class ThreadLocalType<T> {

        [ThreadStatic]
        private static T[] buffer;

        public static T[] Buffer
        {
            get
            {
                if (buffer == null) {
                    buffer = new T[1];
                }
                return buffer;
            }
        }
    }

    public static TTarget Reinterpret<TTarget, TSource>(TSource source)
    {
        TSource[] sourceBuffer = ThreadLocalType<TSource>.Buffer;
        TTarget[] targetBuffer = ThreadLocalType<TTarget>.Buffer;

        int sourceSize = Buffer.ByteLength(sourceBuffer);
        int destSize = Buffer.ByteLength(targetBuffer);
        if (sourceSize != destSize) {
            throw new ArgumentException("Cannot convert " + typeof(TSource).FullName + " to " + typeof(TTarget).FullName + ". Data types are of different sizes.");
        }

        sourceBuffer[0] = source;
        Buffer.BlockCopy(sourceBuffer, 0, targetBuffer, 0, sourceSize);
        return targetBuffer[0];
    }
}

class Program {
    static void Main(string[] args)
    {
        Console.WriteLine("Float: " + Cast.Reinterpret<int, float>(100));
        Console.ReadKey();
    }
}
like image 38
hangar Avatar answered Nov 15 '22 19:11

hangar