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
和回来?
你的问题不完全清楚-但引用只对单个对象,数组对象"知道"其实际类型…你不能把uint[]
当作byte[]
来处理,反之亦然。
现在你可以做的是把它隐藏在另一个对象后面。您可以在ByteArrayView
和UInt32ArrayView
之间共享单个字节数组,其中这些类每个都知道如何处理单个底层字节数组。你必须自己编写这些类——我不相信它们存在于现有框架的任何地方。
你可能会创建一个抽象类或接口,这些完全不同的类也会实现,例如
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]);