c#中的引用数组

本文关键字:数组 引用 | 更新日期: 2023-09-27 17:50:56

我有字节数组:

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

之后,我需要将block32转换回byte数组。

问题是我是否可以让block32数组只是我的byte数组的32位参考数组,以避免转换为UInt32和回来?

c#中的引用数组

你的问题不完全清楚-但引用只对单个对象,数组对象"知道"其实际类型…你不能把uint[]当作byte[]来处理,反之亦然。

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

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

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

(或者只是适当地实现IList<T>…)

我从Omer more那里找到了一个答案。这基本上使用不安全的代码来临时更改数组认为它是的类型。

的好处是,您不需要重写任何其他代码来使用不安全的指针或包装器类。缺点是,这不仅是不安全的代码,而且是混乱类型系统的讨厌的不安全代码。

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代码;只是使用Union工作良好,除了(a)调试器检查器感到困惑,(b)长度不更新。如果你能告诉你的方法只使用数组的四分之一,你就成功了。

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

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

那么,什么是不够的呢?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]);