C#中的reinterpret_cast

And*_*eas 4 c# arrays casting

我正在寻找一种方法来重新解释byte []类型的数组作为一个不同的类型,比如short [].在C++中,这可以通过简单的转换实现,但在C#中,我没有找到实现这一目的的方法,而不需要复制整个缓冲区.

有任何想法吗?

San*_*der 10

你可以做到这一点,但这是一个相对糟糕的主意.像这样的原始内存访问不是类型安全的,只能在完全信任的安全环境下完成.您绝不应该在设计合理的托管应用程序中执行此操作.如果您的数据伪装成两种不同的形式,那么您实际上可能有两个独立的数据集?

无论如何,这里有一个快速简单的代码片段来完成你的要求:

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]);
        }
    }
}
Run Code Online (Sandbox Code Playgroud)


han*_*gar 7

这个问题有四个很好的答案。每个都有不同的缺点。当然,要注意字节顺序并意识到所有这些答案都是类型系统中的漏洞,而不是特别危险的漏洞。简而言之,不要经常这样做,并且只有在您真正需要时才这样做。

  1. 桑德的回答。使用不安全代码重新解释指针。这是最快的解决方案,但它使用了不安全的代码。并不总是一种选择。

  2. 列奥尼达的回答。使用StructLayoutFieldOffset(0)将结构转换为联合。这样做的缺点是一些(罕见的)环境不支持 StructLayout(例如 Flash 构建在 Unity3D 中)并且 StructLayout 不能与泛型一起使用。

  3. ljs的回答。使用BitConverter方法。这样做的缺点是大多数方法分配内存,这在低级代码中不是很好。此外,没有一套完整的这些方法,因此您不能真正通用地使用它。

  4. Buffer.BlockCopy两个不同类型的数组。唯一的缺点是你需要两个缓冲区,这在转换数组时是完美的,但在转换单个值时会很痛苦。请注意长度以字节为单位指定,而不是元素。Buffer.ByteLength有帮助。此外,它仅适用于原语,如整数、浮点数和布尔值,而不适用于结构体或枚举。

但是你可以用它做一些巧妙的事情。

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();
    }
}
Run Code Online (Sandbox Code Playgroud)