cs
#region Blittable 缓存检测器
public static class BlittableChecker
{
private static class Cache<T> where T : struct
{
public static readonly bool IsBlittable = CheckInternal();
/// <summary>
/// 可直接封送类型
/// </summary>
/// <returns></returns>
private static bool CheckInternal()
{
T dummy = default;
GCHandle handle;
try
{
handle = GCHandle.Alloc(dummy, GCHandleType.Pinned);
}
catch (ArgumentException)
{
return false;
}
handle.Free();
return true;
}
}
/// <summary>
/// 判断值类型是否 Blittable,每种 T 只校验一次,全局缓存
/// </summary>
public static bool IsBlittable<T>() where T : struct
{
return Cache<T>.IsBlittable;
}
}
#endregion
#region 泛型数组锁定工具类
public static unsafe class BufferLockHelper
{
/// <summary>
/// 锁定结构体数组,回调内瞬时获取指针(fixed,同步使用,不可缓存指针)
/// </summary>
/// <typeparam name="T">blittable struct</typeparam>
/// <param name="imgData">目标数组</param>
/// <param name="act">回调:指针,元素个数,总字节大小</param>
public static void LockStructArray<T>(T[] imgData, Action<IntPtr, int, int> act)
where T : struct
{
if (imgData == null)
throw new ArgumentNullException(nameof(imgData));
if (imgData.Length == 0)
return;
if (!BlittableChecker.IsBlittable<T>())
{
throw new NotSupportedException(
$"结构体 {typeof(T).FullName} 不是 Blittable,禁止将 fixed 裸指针传递给非托管代码!");
}
int elemSize = Marshal.SizeOf(typeof(T));
int totalBytes = elemSize * imgData.Length;
fixed (void* ptr = imgData)
{
act(new IntPtr(ptr), imgData.Length, totalBytes);
}
}
}
#endregion
不能封送
cs
public struct BadStructWithBool
{
public int X;
public bool Valid;
}
第一次改造,可以封送,但是不能Pinned
cs
// 这样改【无效】,仍然不能Pinned、不能通过 BlittableChecker
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct StillBad
{
public int X;
[MarshalAs(UnmanagedType.U1)]
public bool Valid; // 字段本质仍是 bool,CLR判定为non-blittable
}
第二次改造后 ,可以封送
cs
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct GoodStructWithBool
{
public int X;
private byte _valid; // 底层用 byte(blittable)
// 对外 bool 访问器,不影响内存布局
public bool Valid
{
get => _valid != 0;
set => _valid = (byte)(value ? 1 : 0);
}
}