使用 fixed 关键字固定零长度数组
本文关键字:数组 fixed 关键字 使用 | 更新日期: 2023-09-27 18:36:03
我有一些无法更改的unsafe
C# 代码,它公开了如下方法:
static unsafe void Foo(
byte* a, int aLength,
byte* b, int bLength,
byte* c, int cLength,
byte* d, int dLength,
byte* e, int eLength);
我像这样调用这些方法:
static void Bar(
byte[] a, int aOffset, int aLength,
byte[] b, int bOffset, int bLength,
byte[] c, int cOffset, int cLength,
byte[] d, int dOffset, int dLength,
byte[] e, int eOffset, int eLength)
{
fixed (byte* a_ = &a[aOffset])
fixed (byte* b_ = &b[bOffset])
fixed (byte* c_ = &c[cOffset])
fixed (byte* d_ = &d[dOffset])
fixed (byte* e_ = &e[eOffset])
{
Foo(a_, aLength,
b_, bLength,
c_, cLength,
d_, dLength,
e_, eLength);
}
}
(为简洁起见,省略了参数验证。
除非其中一个字节数组的长度为零,否则这很有效。在这种情况下,我得到一个IndexOutOfRangeException。
索引超出数组的范围。
如何防止异常,最好不要编写大量样板代码,也不从fixed
切换到其他内容?
不安全的方法不会读取或写入长度为零的参数。
您不能访问空数组中的任何元素,因此请将任何空数组替换为包含可以访问的元素的虚拟数组:
if (aLength == 0) a = new int[1];
if (bLength == 0) b = new int[1];
if (cLength == 0) c = new int[1];
if (dLength == 0) d = new int[1];
if (eLength == 0) e = new int[1];
由于它不读取或写入长度为零的数组,因此您可以更改
fixed (byte* a_ = &a[aOffset])
fixed (byte* b_ = &b[bOffset])
fixed (byte* c_ = &c[cOffset])
fixed (byte* d_ = &d[dOffset])
fixed (byte* e_ = &e[eOffset])
自
fixed (byte* a_ = (a.Length == 0 ? (byte*)IntPtr.Zero : &a[aOffset]))
fixed (byte* b_ = (b.Length == 0 ? (byte*)IntPtr.Zero : &b[aOffset]))
fixed (byte* c_ = (c.Length == 0 ? (byte*)IntPtr.Zero : &c[aOffset]))
fixed (byte* d_ = (d.Length == 0 ? (byte*)IntPtr.Zero : &d[aOffset]))
fixed (byte* e_ = (e.Length == 0 ? (byte*)IntPtr.Zero : &e[aOffset]))
但是,如果执行此操作,然后尝试读取或写入其中一个空数组,则会出现 nullpointer 异常。
is
static void Bar( ... )
{
byte[] dummy = new byte[1];
fixed (byte* a_ = a.Lenght>0 ? &a[aOffset] : &dummy[0])
...
}
太笨重了?
编辑:
仅出于历史原因保留上述内容,?
运算符将不起作用。
所以你需要做没有fixed
关键字并做一些类似的事情
public unsafe struct UnsafePointerStruct
{
public GCHandle gch;
public byte* addr;
}
static unsafe UnsafePointerStruct GetUnsafePointer(Array a, int ai)
{
UnsafePointerStruct ups=new UnsafePointerStruct();
ups.gch=GCHandle.Alloc(a,GCHandleType.Pinned);
if (a.Length<=ai) ups.addr=(byte*)IntPtr.Zero;
ups.addr=(byte*)Marshal.UnsafeAddrOfPinnedArrayElement(a,ai);
return ups;
}
static unsafe void Bar(
byte[] a, int aOffset, int aLength,
byte[] b, int bOffset, int bLength,
byte[] c, int cOffset, int cLength,
byte[] d, int dOffset, int dLength,
byte[] e, int eOffset, int eLength)
{
UnsafePointerStruct upsa=GetUnsafePointer(a,aOffset);
UnsafePointerStruct upsb=GetUnsafePointer(b,bOffset);
UnsafePointerStruct upsc=GetUnsafePointer(c,cOffset);
UnsafePointerStruct upsd=GetUnsafePointer(d,dOffset);
UnsafePointerStruct upse=GetUnsafePointer(e,eOffset);
Foo(upsa.addr, aLength,
upsb.addr, bLength,
upsc.addr, cLength,
upsd.addr, dLength,
upse.addr, eLength);
upsa.gch.Free();
upsb.gch.Free();
upsc.gch.Free();
upsd.gch.Free();
upse.gch.Free();
}