Inside C# 4 Data Structs |
Written by Mike James | |||||||
Tuesday, 07 September 2010 | |||||||
Page 4 of 6
Serialising structsNow that we have looked at the complicated question of how to control the memory layout of a struct, it is time to discover how to get at the bytes that make up a struct, i.e. how do we serialise a struct? There are many ways of doing this job and the most commonly encountered uses Marshal.AllocHGlobal to allocate an unmanaged buffer from the global heap. After this everything is achieved using memory transfer functions such as StructToPtr or Copy. For exampl public static byte[] RawSerialize( In fact there is no need to do so much bit moving as it is fairly easy to move the bytes in the struct directly to the byte array without the need for an intermediate buffer. The key to this generally useful technique is the GCHandle object. This will return a Garbage Collection handle to any managed data type. If you ask for a “pinned” handle then the object will not be moved by the garbage collector and you can use the handle’s AddrOfPinnedObject method to retrieve its starting address. For example the RawSerialise method can be rewritten:
public static byte[] RawSerialize( This is both simpler and faster. You can use the same methods to deserialise data in a byte array into a struct but rather than considering this example it is more instructive to examine the related problem of reading a struct from a stream. Structs from streamsA fairly common requirement is to read a struct, possibly written using some other language, into a C# struct. For example, suppose you need to read in a bitmap file which starts with a file header, followed by a bitmap header and then the bitmap data. The file header structure is easy to translate: [StructLayout(LayoutKind.Sequential, A function that will read any structure available as a stream and return a struct can be written without the need for Generics: public object ReadStruct(FileStream fs, You should recognise the use of the GCHandle object to enable the data to be transferred. The new feature is the use of a Type object to specify the type of the struct being read in. Unfortunately there is no way to use this to return an object of the specified type and so we need to use a cast when calling the function, as in: FileStream fs = new FileStream( If we want to avoid the cast then we need to create a generic method. This is just a matter of introducing a type parameter <T> and then using it throughout the method as if it was the type of the struct : public T ReadStruct <T> (FileStream fs) Notice that now we have to cast the object returned by PtrToStructure to the type in the method rather than in the method call which becomes: BITMAPFILEHEADER bmFH = It is interesting to contemplate just how much better the generic method is than the method that needs the explicit cast. <ASIN:1430229799> <ASIN:0262201755> <ASIN:0596800959> <ASIN:047043452X> <ASIN:193435645X> <ASIN:0596007124> |
|||||||
Last Updated ( Tuesday, 28 September 2010 ) |