6

我有字节数组:

byte[] alldata = new byte[1024];

然后我需要将此数据转换为UInt32

UInt32[] block32 = new UInt32[(int)Math.Ceiling((double)alldata.Length / 4)];
Buffer.BlockCopy(alldata, 0, block32, 0, alldata.Length);

在此之后我需要转换block32byte数组。

问题是我是否可以让block32数组只是我的数组的 32 位引用数组byte避免相互转换UInt32

4

3 回答 3

12

您的问题并不完全清楚-但引用仅针对单个对象,而数组对象“知道”其真实类型……您不能只将 auint[]视为 a byte[],反之亦然。

现在你可以做的就是把它隐藏在另一个对象后面。ByteArrayView您可以拥有一个在 an和 an之间共享的单字节数组,UInt32ArrayView其中每个类都知道如何寻址单个底层字节数组。不过,您必须自己编写这些类——我不相信它们存在于现有框架中的任何地方。

您可能会创建一个抽象类或接口,这些不同的类也将实现,例如

class ByteArrayView : IArrayView<byte>
class UInt32ArrayView : IArrayView<uint>

(或者只是IList<T>适当地实施,当然......)

于 2013-01-07T11:42:01.947 回答
1

我已经修改了Omer Mor找到的答案。这基本上使用不安全代码来临时更改数组认为的类型。

从好的方面来说,您不需要重写任何其他代码来使用不安全的指针或包装类。不利的一面是,这不仅是不安全的代码,而且是与类型系统混淆的讨厌的不安全代码。

static class ArrayCaster
{
    [StructLayout(LayoutKind.Explicit)]
    private struct Union
    {
        [FieldOffset(0)]
        public Byte[] Bytes;
        [FieldOffset(0)]
        public UInt32[] UInt32s;
    }

    static unsafe int UInt32Id
    {
        get
        {
            fixed (UInt32* pUints = new UInt32[1])
            {
                var pBytes = (Byte*)pUints;
                return *(int*)(pBytes - 8);
            }
        }
    }

    static unsafe void SetSizeAndId(
        byte[] bytes,
        int newSize, int newId,
        out int oldSize, out int oldId)
    {
        fixed (Byte* pBytes = bytes)
        {
            int* size = (int*)(pBytes - 4);
            oldSize = *size;
            *size = newSize;
            int* id = (int*)(pBytes - 8);
            oldId = *id;
            *id = newId;
        }
    }

    public static void WithBytesAsUInt32s(
        Byte[] bytes, Action<UInt32[]> withUints)
    {
        if (bytes.Length % sizeof(UInt32) != 0) throw new ArgumentException();

        Union u = new Union { Bytes = bytes };
        int origSize, origId;
        SetSizeAndId(bytes, bytes.Length / sizeof(UInt32), UInt32Id,
                     out origSize, out origId);
        try
        {
            withUints(u.UInt32s);
        }
        finally
        {
            SetSizeAndId(bytes, origSize, origId, out origSize, out origId);
        }
    }
}

在您的代码中:

byte allData = new byte[1024];
ArrayCaster.WithBytesAsUInt32s(allData, uintArray => Encrypt(unitArray));

编辑:如果您的加密/解密方法采用显式长度以及数组,您可以完全UInt32删除unsafe代码;只是使用工作正常,除了(a)调试器检查器混淆,(b)长度不更新。如果你能告诉你的方法只使用数组的四分之一,那么你就是黄金。Union

于 2013-01-07T12:23:22.907 回答
0

正如您已经提到的,您将使用uint[]for 加密。

由于某些原因,您使用BlockCopy. 但是您想像访问byte[]一样访问uint[],而不转换或转换整个数组。

那么,有什么不足呢?C#中有什么东西可以做到这一点吗?

再想一小时后,我猜你想要它的目的,应该是indexer.

这是代码。这很简单。

public partial class UIntIndexer {
    static int ComputeElementCount(int size, int bytesCount) {
        var r=~0;
        return Math.DivRem(bytesCount, size, out r)+(r>0?1:0);
    }

    static int ComputeIndex(int index) {
        return sizeof(uint)*index;
    }

    public UIntIndexer(byte[] array) {
        m_Length=ComputeElementCount(sizeof(uint), (m_Array=array).Length);
    }

    public uint this[int index] {
        set {
            var startIndex=ComputeIndex(index);
            var bytes=BitConverter.GetBytes(value);
            var count=m_Length>1+index?sizeof(uint):m_Array.Length-startIndex;
            Buffer.BlockCopy(bytes, 0, m_Array, startIndex, count);
        }

        get {
            var startIndex=ComputeIndex(index);

            if(m_Length>1+index)
                return BitConverter.ToUInt32(m_Array, startIndex);
            else {
                var bytes=new byte[sizeof(uint)];
                Buffer.BlockCopy(m_Array, startIndex, bytes, 0, m_Array.Length-startIndex);
                return BitConverter.ToUInt32(bytes, 0);
            }
        }
    }

    public int Length {
        get {
            return m_Length;
        }
    }

    byte[] m_Array;
    int m_Length;
}

如果应该考虑线程安全,您可能需要保持源数组同步。以下是测试它的代码:

var alldata=new byte[] { 1, 2, 3, 4, 5, 6, 7, 8, 9 };
var block32=new byte[alldata.Length];
var uintIndexer=new UIntIndexer(block32);
Buffer.BlockCopy(alldata, 0, block32, 0, alldata.Length);

Debug.Print("uintIndexer.Length={0}", uintIndexer.Length);

for(var i=uintIndexer.Length; i-->0; )
    Debug.Print("uintIndexer[{0}]={1}", i, uintIndexer[i]);
于 2013-01-07T15:11:48.363 回答