简体   繁体   中英

Serialize unmanaged generic type in C# 7.3

The unmanaged keyword added to C# 7.3 is designed largely for interop. But it is also said that this might facilitate generic serialization methods. Similarly, this answer shows part of how this might be done -- in particular, to deserialize an array of unmanaged elements.

I am wondering whether this is safe to do. There are at least two concerns. First, it seems based on my experimentation that the sizeof(T) operator still must be used in unsafe context, even where the T : unmanaged constraint applies. This seems consistent with the proposition that even with the unmanaged constraint, different platforms might lay out unmanaged structs differently (unless, for example, LayoutKind.Sequential is used), so sizeof is unreliable for serialization purposes, as was the case before . And, of course, we must use unsafe context to get the bytes underlying the generic T.

Second, numeric types may also have different layouts on different platforms. Microsoft's code using Span thus includes methods that will specifically read / write with a specified endianness, such as here . If we serialize an unmanaged T on one platform and deserialize on another, then it would seem that there is a risk that we will get back numbers with different endianness.

My conclusion, then, is that simply serializing the underlying bytes of T is not safe, unless one has some assurance that the deserialization will take place on the same platform. Is that correct?

Assuming it is, the related question is how we might still serialize an unmanaged T generically. Presumably, we could use reflection on T to figure out which types are used, and we can then determine the layout of T using unsafe code . Then, taking into account whether the current system is little endian or big endian, it should be possible to craft an algorithm that will serialize and deserialize a generic unmanaged T. Is that correct? (Of course, if someone has done this, that would be ideal.)

This blog post and the associated code illustrate how to serialize and deserialize structs that are declared to be unmanaged. One approach to serialization is as follows:

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

The corresponding deserialization code is:

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

As expected, this produces considerable speed gains documented in the blog post. Thus, this may be a useful serialization technique when data is stored as a Span of an unmanaged type. The technique does assume, however, that values are deserialized on a platform with the same endianness as the platform on which it was serialized. For some applications, this is unlikely to be a problem, given the dominance of little endian machines. If this is an issue, one can check for a big endian machine and then convert values before serializing and after deserializing.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM