Working with arrays within a struct in C #

Source: Internet
Author: User
Tags arrays bool constructor header mscorlib pack reflection serialization
Arrays are heavily doped with the structure of common types and arrays, such as the Image_optional_header structure defining the header structure of a PE file, in C + + code as follows:


The following are program code:

typedef struct _IMAGE_DATA_DIRECTORY {
DWORD virtualaddress;
DWORD Size;
} image_data_directory, *pimage_data_directory;

#define Image_numberof_directory_entries 16

typedef struct _IMAGE_OPTIONAL_HEADER {

WORD Magic;

//...

DWORD numberofrvaandsizes;
Image_data_directory Datadirectory[image_numberof_directory_entries];

} Image_optional_header32, *pimage_optional_header32;



It is perfectly correct to use arrays in a structure like this in C + +, because these arrays will be part of the entire structure and directly access the memory block where the structure operates. However, in languages such as C #, this cannot be used directly because arrays exist as a special type of reference, such as the definition:
The following are program code:

public struct image_data_directory
{
public UINT virtualaddress;
public UINT Size;
}

public struct Image_optional_header
{
public const int image_numberof_directory_entries = 16;

public ushort Magic;

//...

public UINT numberofrvaandsizes;

Public Image_data_directory datadirectory[image_numberof_directory_entries];
}



In C # This is an error in defining an array in a struct, and a CS0650 error is obtained at compile time:

The following are references:

Error CS0650: syntax error, incorrect array declaration. To declare a managed array, the rank descriptor should precede the variable identifier




If you instead use a similar definition syntax for reference types in C #, such as
The following are program code:

public struct Image_optional_header
{
public const int image_numberof_directory_entries = 16;

public ushort Magic;

//...

public UINT numberofrvaandsizes;

Public image_data_directory[] DataDirectory = new Image_data_directory[image_numberof_directory_entries];
}



You get a CS0573 error:

The following are references:

Error CS0573: "Image_optional_header." DataDirectory ": Cannot have instance field initializer in structure




This is not the same as class initialization because the initialization of a reference type cannot be within the structure. In this way, only the initialization of the array can be placed in the constructor, and the structure can not have a default constructor without parameters, it's a hassle, huh?
The following are program code:

public struct Image_optional_header
{
public const int image_numberof_directory_entries = 16;

public ushort Magic;

public UINT numberofrvaandsizes;

Public image_data_directory[] DataDirectory;

Public Image_optional_header (IntPtr ptr)
{
Magic = 0;
numberofrvaandsizes = 0;

DataDirectory = new Image_data_directory[image_numberof_directory_entries];
}
}



This may seem to be the case, but if you use Marshal.SizeOf (typeof Image_optional_header) you will see that the length is fundamentally different from the length defined in C + +. The problem is still the array in the structure, although it seems that the array is defined within the structure, but in fact there is only one pointer to the image_data_directory[array type in this structure, which should be stored in the DataDirectory unknown array content, in the managed heap.
The question then becomes how to place an array of reference types in the structure of a value type.

There are many solutions, such as defining content by StructLayout explicitly specifying the length of the structure:
The following are program code:

[StructLayout (LayoutKind.Sequential, size=xxx)]
public struct Image_optional_header
{
public const int image_numberof_directory_entries = 16;

public ushort Magic;

public UINT numberofrvaandsizes;

Public Image_data_directory DataDirectory;
}



Notice here StructLayout size Specifies the length of the entire structure, because the DataDirectory is already the last field, so the latter 15 elements of the array are saved in the unnamed stack space. It is a bit cumbersome to use, you need to read the entire structure at once, and then access the other array elements after the DataDirectory field by unsafe the code's pointer operation.
The advantage of this approach is that it is simple to define, but it needs to rely on the unsafe pointer manipulation code, and that the array field must be at the final limit. Of course, you can also use Layoutkind.explicit to explicitly specify the unknown of each field to simulate multiple structures in-line arrays, but this requires a manual calculation of each field offset, which is more cumbersome.

Another solution is to explicitly define the position of an array element by Marshal support, such as
The following are program code:

[StructLayout (LayoutKind.Sequential, pack=1)]
public struct Image_optional_header
{
public const int image_numberof_directory_entries = 16;

public ushort Magic;

public UINT numberofrvaandsizes;

[MarshalAs (UnmanagedType.ByValArray, sizeconst=image_numberof_directory_entries)]
Public image_data_directory[] DataDirectory;
}



This approach is relatively elegant, using the properties supported by the Marshal mechanism to define the value array semantics, which is not much different from the normal array. The array definitions described above are compiled into IL definitions:
The following are program code:

. field public marshal (fixed array []) valuetype image_data_directory[] DataDirectory



Although the type is still valuetype image_data_directory[], this array has changed from the citation semantics to the value semantic because of the adornment of Marshal (fixed array [16]). However, this will still be subject to a number of restrictions, such as not multi-layer nesting, performance affected when using, and so on.

In addition to the above two kinds of solution in the structure of the definition itself, but also from the structure of the operation of the fuss.

In addition to accessing an array within a structure, the main type of operation is to read the entire structure from a memory block or an input stream, so it is entirely possible to use the CLR's enhanced binary serialization support to complete the loading and saving of data by implementing a custom serialization function, such as:
The following are program code:


[Serializable]
public struct image_optional_header:iserializable
{
public const int image_numberof_directory_entries = 16;

public ushort Magic;

public UINT numberofrvaandsizes;

Public image_data_directory[] DataDirectory;

Public Image_optional_header (IntPtr ptr)
{
Magic = 0;
numberofrvaandsizes = 0;

DataDirectory = new Image_data_directory[image_numberof_directory_entries];
}

[SecurityPermissionAttribute (Securityaction.demand,serializationformatter=true)]
public virtual void GetObjectData (SerializationInfo info, StreamingContext context)
{
Complete serialization operation
}
}



This workaround separates the load and storage of the structure from the internal performance of the structure. Although the structure internally saves only the array references, the user does not need to be concerned. But the disadvantage is that you must write the appropriate serialization support code for each structure, writing and maintenance are cumbersome.

Similar to this idea is my preferred solution, through a common tool base class in a Reflection way of unified processing, such as:
The following are program code:

public class Image_optional_header:binaryblock
{
public const int image_numberof_directory_entries = 16;

public ushort Magic;

public UINT numberofrvaandsizes;

Public image_data_directory[] DataDirectory = new Image_data_directory[image_numberof_directory_entries];
}



Notice that the original struct has been changed to class here, because in this way there is no need to stick to the memory model of the value type. Binaryblock is a common tool base class that is responsible for providing type loading and storage capabilities through Reflection, such as
The following are program code:

public class Binaryblock
{
private static readonly ILog _log = Logmanager.getlogger (typeof (Binaryblock));

Public Binaryblock ()
{
}

Static public Object Loadfromstream (BinaryReader reader, Type ObjType)
{
if (Objtype.equals (typeof (Char))
{
Return reader. Readchar ();
}
else if (Objtype.equals (typeof (Byte))
{
Return reader. ReadByte ();
}
//...
else if (Objtype.equals (typeof double))
{
Return reader. Readdouble ();
}
else if (Objtype.isarray)
{
Handling the array
}
Else
{
foreach (FieldInfo field in Classtype.getfields ())
{
Field. SetValue (obj, Loadfromstream (...));
}
}

return true;
}

public bool Loadfromstream (Stream stream)
{
Return Loadfromstream (The new BinaryReader (stream), this);
}
}



Loadfromstream is a nested method that loads the corresponding values from the stream according to the specified field type. When used only when this method is called for the entire type, it is automatically processed with the Reflection mechanism, traversing all the fields of the class, and can be handled directly if there is a nested definition. By using this method, the definition of the type itself is essentially unnecessary to worry about loading and storing mechanisms, as long as you inherit from the Binaryblock type. Interested friends You can also extend this class further to support the binary serialization mechanism.

In addition, the C # 2.0 provides a new fixed array mechanism for solving such problems, supporting arrays that directly define inline value semantics in structs, such as
The following are program code:

struct data
{
int header;
fixed int values[10];
}



This structure is compiled by the compiler to translate an array field into an external value type structure to achieve the appropriate space layout, such as
The following are program code:

. class private sequential ANSI sealed beforefieldinit data
extends [Mscorlib]system.valuetype
{
. class sequential ANSI sealed nested public beforefieldinit ' <values>e__fixedbuffer0 '
extends [Mscorlib]system.valuetype
{
. pack 0
. Size 40
. custom instance void [Mscorlib]system.runtime.compilerservices.compilergeneratedattribute::.ctor () = (01 00 00 00
. Field public Int32 Fixedelementfield
}//End of class ' <values>e__fixedbuffer0 '

. field public Int32 header
. field public valuetype data/' <values>e__fixedbuffer0 ' values
. custom instance void [Mscorlib]system.runtime.compilerservices.fixedbufferattribute::.ctor (class [mscorlib] System.Type, Int32) = (...)
}//End of class data



You can see that the values field is compiled into a value type, and the value type itself uses a thought similar to the first of these solutions, forcing the length of the structure to be limited. When used, it is a unsafe operation similar to the first workaround, such as access to this array being compiled into a unsafe pointer operation:
The following are program code:

Before compiling
for (int i=0; i<10; i++)
D.values[i] = i;

After compiling
for (int i=0; i<10; i++)
&data1.values.fixedelementfield[(((INTPTR) i) * 4)] = i;



Unfortunately, this approach must be compiled unsafe, because it is implemented internally through the unsafe approach. Also, only one level of nested definitions can be processed, and a CS1663 error can be obtained if the Image_optional_header definition is converted:
The following are program code:

Error cs1663:fixed sized buffer type must is one of the Following:bool, Byte, short, int, long, char, sbyte, ushort, UIn T, ulong, float or double




Eric Gunnerson has an article, Arrays inside of structures, that briefly describes this limited enhancement syntax in C # 2.0.


Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.