非托管关键字added to C# 7.3主要用于互操作。但它也said这可能有助于通用序列化方法。同样,this answer显示了如何完成此操作的一部分 - 特别是反序列化非托管元素数组。
我想知道这是否安全。至少有两个问题。首先,根据我的实验,似乎sizeof(T)运算符仍然必须在不安全的上下文中使用,即使T:unmanaged约束适用。这似乎与以下命题一致:即使使用非托管约束,不同平台也可能以不同方式布置非托管结构(例如,除非使用LayoutKind.Sequential),因此sizeof对于序列化目的而言是不可靠的as was the case before。当然,我们必须使用不安全的上下文来获取通用T的基础字节。
其次,数字类型在不同平台上也可能有不同的布局。因此,使用Span的Microsoft代码包含将使用指定的字节序特别读取/写入的方法,例如here。如果我们在一个平台上序列化非托管T并在另一个平台上反序列化,那么似乎存在我们将获得具有不同字节顺序的数字的风险。
我的结论是,简单地序列化T的底层字节是不安全的,除非有人确信反序列化将在同一平台上进行。这是对的吗?
假设是,相关的问题是我们如何仍然可以序列化非托管T一般。据推测,我们可以使用T上的反射来确定使用哪种类型,然后我们可以确定T using unsafe code的布局。然后,考虑到当前系统是小端还是大端,应该可以制作一个算法来序列化和反序列化通用的非托管T.这是正确的吗? (当然,如果有人这样做,那将是理想的。)
答案 0 :(得分:0)
此blog post和associated code说明了如何对声明为非托管的结构进行序列化和反序列化。一种序列化方法如下:
public static void Write<T>(this Stream stream, T value)
where T : unmanaged
{
var tSpan = MemoryMarshal.CreateSpan(ref value, 1);
var span = MemoryMarshal.AsBytes(tSpan);
stream.Write(span);
}
相应的反序列化代码为:
public static T Read<T>(this Stream stream)
where T : unmanaged
{
var result = default(T);
var tSpan = MemoryMarshal.CreateSpan(ref result, 1);
var span = MemoryMarshal.AsBytes(tSpan);
stream.Read(span);
return result;
}
如预期的那样,这会在博客文章中记录到可观的速度提升。因此,当数据存储为非托管类型的跨度时,这可能是有用的序列化技术。但是,该技术确实假定在与序列化平台相同的字节序的平台上反序列化了值。对于某些应用程序,鉴于小字节序机的优势,这不太可能成为问题。如果这是一个问题,则可以在序列化之前和反序列化之后先检查大型endian机器,然后检查convert values。