C#中的reinterpret_cast

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

有任何想法吗?

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

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

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]); } } } 

c#支持这个,只要你愿意使用不安全的代码,但只支持结构。

例如:(框架为您提供此function,但您可以将其扩展为int < - > uint转换

 public unsafe long DoubleToLongBits(double d) { return *((long*) (void*) &d); } 

由于数组是引用类型并保存有关其类型的自己的元数据,因此无法在不覆盖实例上的元数据头的情况下重新解释它们(操作可能会失败)。

你可以如何从foo []中获取foo *并将其转换为bar *(通过上面的技术)并使用它来迭代数组。 执行此操作将要求您在重新解释指针的使用寿命期间固定原始数组。

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

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

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

  3. ljs的回答。 使用BitConverter方法。 这样做的缺点是大多数方法都会分配内存,这在低级代码中并不是很好。 此外,还没有完整的这些方法,所以你不能真正使用它。

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

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

 public static class Cast { private static class ThreadLocalType { [ThreadStatic] private static T[] buffer; public static T[] Buffer { get { if (buffer == null) { buffer = new T[1]; } return buffer; } } } public static TTarget Reinterpret(TSource source) { TSource[] sourceBuffer = ThreadLocalType.Buffer; TTarget[] targetBuffer = ThreadLocalType.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(100)); Console.ReadKey(); } } 

您可以将短路/字节包装到允许您访问这两个值的结构中:

另见: C#中的C ++联合

使用系统;
使用System.Collections.Generic;
使用System.Runtime.InteropServices;

命名空间TestShortUnion {
     [StructLayout(LayoutKind.Explicit)]
     public struct shortbyte {
         public static implicit operator shortbyte(int input){
             if(input> short.MaxValue)
                抛出新的ArgumentOutOfRangeException(“input”,“shortbyte只接受短程中的值”);
            返回新的短字节((短)输入);
         }

         public shortbyte(byte input){
             shortval = 0;
             byteval =输入;
         }

         public shortbyte(short input){
             byteval = 0;
             shortval =输入;
         }

         [FieldOffset(0)]
         public byte byteval;
         [FieldOffset(0)]
        公共空头短期;
     }

    课程{
         static void Main(string [] args){
             shortbyte [] testarray = new shortbyte [] {1,2,3,4,5,6,7,8,9,10,1111};

             foreach(testarray中的shortbyte singleval){
                 Console.WriteLine(“Byte {0}:Short {1}”,singleval.byteval,singleval.shortval);
             }

             System.Console.ReadLine();
         }
     }
 }

像这样的转换从根本上说是不安全的,并且不允许使用托管语言。 这也是C#不支持工会的原因。 是的,解决方法是使用Marshal类。

这种行为会导致C#相当类型不安全。 但是,您可以通过类型安全的方式轻松实现此目的(当然,您正在复制数组)。

如果你想要一个字节映射到一个short,那么使用ConvertAll很简单,例如: –

 short[] shorts = Array.ConvertAll(bytes, b => (short)b); 

如果你想简单地将每2个字节映射到一个short,那么以下内容应该有效:

 if (bytes.Length % 2 != 0) { throw new ArgumentException("Byte array must have even rank."); } short[] shorts = new short[bytes.Length / 2]; for (int i = 0; i < bytes.Length / 2; ++i) { shorts[i] = BitConverter.ToInt16(bytes, 2*i); } 

有可能使用marshaller做一些奇怪的比特来实现这一点,可能使用一个不安全的{...}代码块,虽然这很容易出错并使你的代码无法validation。

我怀疑使用类型安全的习惯用语而不是类型不安全的C / C ++,可以更好地实现你想要做的事情!

更新 :已更新以考虑评论。

难道不可能创建一个实现字节和短路接口的集合类吗? 也许实现IList 和IList ? 然后,您可以让您的底层集合包含字节,但实现对字节对有效的IList 函数。

我使用FastArraySerializer的代码创建一个类型转换器,从SByte []到Double []

 public unsafe class ConvertArrayType { [StructLayout(LayoutKind.Explicit)] private struct Union { [FieldOffset(0)] public sbyte[] sbytes; [FieldOffset(0)] public double[] doubles; } private Union _union; public double[] doubles { get { return _union.doubles; } } public sbyte[] sbytes { get { return _union.sbytes; } } [StructLayout(LayoutKind.Sequential, Pack = 1)] private struct ArrayHeader { public UIntPtr type; public UIntPtr length; } private readonly UIntPtr SBYTE_ARRAY_TYPE; private readonly UIntPtr DOUBLE_ARRAY_TYPE; public ConvertArrayType(Array ary, Type newType) { fixed (void* pBytes = new sbyte[1]) fixed (void* pDoubles = new double[1]) { SBYTE_ARRAY_TYPE = getHeader(pBytes)->type; DOUBLE_ARRAY_TYPE = getHeader(pDoubles)->type; } Type typAry = ary.GetType(); if (typAry == newType) throw new Exception("No Type change specified"); if (!(typAry == typeof(SByte[]) || typAry == typeof(double[]))) throw new Exception("Type Not supported"); if (newType == typeof(Double[])) { ConvertToArrayDbl((SByte[])ary); } else if (newType == typeof(SByte[])) { ConvertToArraySByte((Double[])ary); } else { throw new Exception("Type Not supported"); } } private void ConvertToArraySByte(double[] ary) { _union = new Union { doubles = ary }; toArySByte(_union.doubles); } private void ConvertToArrayDbl(sbyte[] ary) { _union = new Union { sbytes = ary }; toAryDouble(_union.sbytes); } private ArrayHeader* getHeader(void* pBytes) { return (ArrayHeader*)pBytes - 1; } private void toAryDouble(sbyte[] ary) { fixed (void* pArray = ary) { var pHeader = getHeader(pArray); pHeader->type = DOUBLE_ARRAY_TYPE; pHeader->length = (UIntPtr)(ary.Length / sizeof(double)); } } private void toArySByte(double[] ary) { fixed (void* pArray = ary) { var pHeader = getHeader(pArray); pHeader->type = SBYTE_ARRAY_TYPE; pHeader->length = (UIntPtr)(ary.Length * sizeof(double)); } } } // ConvertArrayType{} 

这是VB的用法:

 Dim adDataYgch As Double() = Nothing Try Dim nGch As GCHandle = GetGch(myTag) If GCHandle.ToIntPtr(nGch) <> IntPtr.Zero AndAlso nGch.IsAllocated Then Dim asb As SByte() asb = CType(nGch.Target, SByte()) Dim cvt As New ConvertArrayType(asb, GetType(Double())) adDataYgch = cvt.doubles End If Catch ex As Exception Debug.WriteLine(ex.ToString) End Try