In the example C# code below, I have a byte array which has been read from a socket. I want to parse the data into the various fields of 'exampleClass' (first 8 bytes into the 64-bit variable 'field1', next 4 bytes into 32-bit variable 'field2', etc.)
在下面的示例C#代码中,我有一个从套接字读取的字节数组。我想将数据解析为'exampleClass'的各个字段(前8个字节到64位变量'field1',接下来4个字节到32位变量'field2'等)
using System;
namespace CsByteCopy
{
class Program
{
class ExampleClass
{
public UInt64 field1;
public UInt32 field2;
public UInt16 field3;
public byte[] field4 = new byte[18];
}
static void Main(string[] args)
{
byte[] exampleData =
{
// These 8 bytes should go in 'field1'
0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,
// These 4 bytes should go in 'field2'
0x08,0x09,0x0A,0x0B,
// These 2 bytes should go in 'field3'
0x0C,0x0D,
// These 18 * 1 bytes should go in 'field4'
0x0E,0x0F,0x10,0x11,0x12,0x13,0x14,0x15,0x16,0x17,0x18,0x19,0x1A,0x1B,0x1C,0x1D,0x1E,0x1F,
};
ExampleClass exampleClass = new ExampleClass();
// Perform copy
}
}
}
It's been a long time since I last used C, but if I recall correctly, I might have been able to get away with a single memcpy() call to populate all the fields in the class. What's the most efficient way of populating the fields of 'exampleClass' in C#?
自从我上次使用C以来已经很长时间了,但是如果我没记错的话,我可能已经能够通过单个memcpy()调用来填充类中的所有字段。在C#中填充'exampleClass'字段的最有效方法是什么?
7 个解决方案
#1
13
You have a lot of options, the one that turns out the best usually depends on what your program needs/can handle and exactly how much speed you want. There are a lot of articles that explain the different ways you can populate a class or a struct with data.
你有很多选择,最好的选择通常取决于你的程序需要/可以处理什么以及你想要多少速度。有很多文章解释了使用数据填充类或结构的不同方法。
Binary Serialization is one way. That requires you write a custom serializer, which is fairly easy. An MSDN article regarding that shows it's not too difficult.
二进制序列化是一种方法。这需要您编写自定义序列化程序,这非常简单。有关这方面的MSDN文章显示它并不太难。
You can write a private constructor that takes in the byte array and uses a BinaryReader or the BitConverter class to read from the byte array (you'd have to wrap it in a MemoryStream
for BinaryReader
) or to simply convert sections of the byte array to the values you need (BitConverter
). In the case of BitConverter
you would also need to use Buffer.BlockCopy
to copy the remaining data of the byte array to the byte array field in your class
您可以编写一个私有构造函数,它接受字节数组并使用BinaryReader或BitConverter类从字节数组中读取(您必须将其包装在MemoryStream中用于BinaryReader)或简单地将字节数组的部分转换为你需要的值(BitConverter)。在BitConverter的情况下,您还需要使用Buffer.BlockCopy将字节数组的剩余数据复制到类中的字节数组字段
A third way, also generally the fastest way, is to convert your class to a struct and use unsafe code to cast the byte array as that struct. Something like this:
第三种方法,通常也是最快的方法,是将您的类转换为结构并使用不安全的代码将字节数组转换为该结构。像这样的东西:
unsafe struct ExampleClass
{
public ulong field1;
public uint field2
public ushort field3
public fixed byte field4[18];
public static ExampleClass ReadStruct(byte[] data)
{
fixed (byte* pb = &data[0])
{
return *(ExampleClass*)pb;
}
}
}
Of course, the above code is only valid if you can use unsafe code. Furthermore, converting the class to a struct may also not be what you're looking for. In most cases, structs are passed by value to functions, so that calling methods copies the entire struct (in this case 32 bytes) instead of passing a reference (4 or 8 bytes, depending on CPU architecture) and can reduce the efficiency of your program. There are exceptions to structs being passed by value, and you can use your favorite search engine for that.
当然,上述代码仅在您使用不安全代码时才有效。此外,将类转换为结构也可能不是您正在寻找的。在大多数情况下,结构体通过值传递给函数,因此调用方法复制整个结构(在这种情况下为32个字节)而不是传递引用(4或8个字节,具体取决于CPU体系结构),并且可能降低您的效率程序。结构通过值传递有例外,您可以使用自己喜欢的搜索引擎。
If you can't use unsafe code, there is also Marshal.PtrToStructure
which will do the same as the above code, but about 10 times slower. You would also need to use the MarshalAs
attribute to specify the size of the array, instead of using the fixed keyword (which is unsafe). At that point, you might as well use the BinaryReader/BitConverter, as it will be faster than the marshal class.
如果你不能使用不安全的代码,那么还有Marshal.PtrToStructure,它将与上面的代码一样,但大约慢10倍。您还需要使用MarshalAs属性来指定数组的大小,而不是使用fixed关键字(这是不安全的)。此时,您可以使用BinaryReader / BitConverter,因为它将比编组类更快。
#2
3
I think you want this:
我想你想要这个:
Here by changing the buffer in field4 you also change the other 3 which are specific types.
这里通过更改field4中的缓冲区,您还可以更改其他3个特定类型。
[StructLayout(LayoutKind.Explicit, Size=8)]
struct UValue
{
[FieldOffset(0)]
public fixed byte field4[18]; // 18 bytes long
[FieldOffset(0)]
public uint64 field1;
[FieldOffset(8)]
public Uint32 field2
[FieldOffset(12)]
public Uint16 field3
}
You might want this:
你可能想要这个:
Here by changing the buffer in fieldload you change the others as above but there is also another byte buffer at the end.
这里通过更改fieldload中的缓冲区,您可以像上面那样更改其他缓冲区,但最后还有另一个字节缓冲区。
[StructLayout(LayoutKind.Explicit, Size=8)]
struct UValue
{
[FieldOffset(0)]
public fixed byte fieldload[38]; // modify this to modify others
[FieldOffset(0)]
public uint64 field1;
[FieldOffset(8)]
public Uint32 field2
[FieldOffset(12)]
public Uint16 field3
[FieldOffset(14)]
public fixed byte field4[18]; // 18 bytes long
}
#3
3
Another option, if you can use a struct, is to Marshal the byte array directly into the structure.
如果可以使用结构,另一个选项是将字节数组直接编组到结构中。
struct ExampleStruct
{
public UInt64 field1;
public UInt32 field2;
public UInt16 field3;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 18)]
public byte[] field4;
}
And to get a struct for that:
并获得一个结构:
var handle = GCHandle.Alloc(exampleData, GCHandleType.Pinned);
var structure = Marshal.PtrToStructure(handle.AddrOfPinnedObject(), typeof (ExampleStruct));
handle.Free();
And structure
will contain your data.
结构将包含您的数据。
#4
1
Forget the efficiency thing and make your code maintainable and readable first. If necessary, profile and improve.
忘记效率的事情,让你的代码首先可维护和可读。如有必要,进行剖析和改进。
Whats wrong with using the BitConverter class.
使用BitConverter类有什么问题。
field1 = BitConverter.ToInt64(exampleData, 0)
field2 = BitConverter.ToInt32(exampleData, 8)
field3 = BitConverter.ToInt16(exampleData, 12)
Array.Copy(exampleData, 14, field4, 0, 18)
#5
1
And now for something completely different ...
现在换一些完全不同的东西......
This does not really answer the OP's question; instead it is something I cooked up to provide a way of mapping a C# struct on top of a byte array, and allow the individual fields in the underlying byte array to be both read and written. It uses unsafe code, and gets a pointer to the byte array and then casts it to a pointer to the struct that maps the fields.
这并没有真正回答OP的问题;相反,它是我做的事情,提供了一种在字节数组之上映射C#结构的方法,并允许读取和写入底层字节数组中的各个字段。它使用不安全的代码,并获取指向字节数组的指针,然后将其转换为指向映射字段的结构的指针。
This may not be all that efficient, but has the advantage of providing a symbolic mapping of the fields without the use of "magic numbers" for the lengths and offsets of the fields. But note that this will not work for data interchange with a system that uses big endian instead of little endian data representation.
这可能不是那么有效,但具有提供字段的符号映射而不使用字段的长度和偏移的“幻数”的优点。但请注意,这对于使用大端而不是小端数据表示的系统进行数据交换不起作用。
The field access methods are implemented as extension methods. See TestMethod() for an example of how to use them.
字段访问方法实现为扩展方法。有关如何使用它们的示例,请参阅TestMethod()。
This was inspired by the answer by Christopher Currens - if you find this useful please give him an upvote.
这是受到Christopher Currens的回答的启发 - 如果你觉得这很有用,请给他一个upvote。
using System;
using System.Runtime.InteropServices;
namespace ConsoleApplication1
{
[StructLayout(LayoutKind.Sequential, Pack = 1)]
unsafe struct ExampleStruct
{
internal const int CField4Length = 18;
public UInt64 Field1;
public UInt32 Field2;
public UInt16 Field3;
public fixed byte Field4[CField4Length];
}
static unsafe class ExampleStructExtensionMethods
{
public static UInt64 GetField1(this byte[] byteArray)
{
fixed (byte* byteArrayPointer = &byteArray[0])
{
return (*(ExampleStruct*)byteArrayPointer).Field1;
}
}
public static UInt32 GetField2(this byte[] byteArray)
{
fixed (byte* byteArrayPointer = &byteArray[0])
{
return (*(ExampleStruct*)byteArrayPointer).Field2;
}
}
public static UInt16 GetField3(this byte[] byteArray)
{
fixed (byte* byteArrayPointer = &byteArray[0])
{
return (*(ExampleStruct*)byteArrayPointer).Field3;
}
}
public static byte[] GetField4(this byte[] byteArray)
{
fixed (byte* byteArrayPointer = &byteArray[0])
{
byte[] field4 = new byte[ExampleStruct.CField4Length];
for (int i = 0; i < ExampleStruct.CField4Length; i++)
field4[i] = (*(ExampleStruct*)byteArrayPointer).Field4[i];
return field4;
}
}
public static void SetField1(this byte[] byteArray, UInt64 field1)
{
fixed (byte* byteArrayPointer = &byteArray[0])
{
(*(ExampleStruct*)byteArrayPointer).Field1 = field1;
}
}
public static void SetField2(this byte[] byteArray, UInt32 field2)
{
fixed (byte* byteArrayPointer = &byteArray[0])
{
(*(ExampleStruct*)byteArrayPointer).Field2 = field2;
}
}
public static void SetField3(this byte[] byteArray, UInt16 field3)
{
fixed (byte* byteArrayPointer = &byteArray[0])
{
(*(ExampleStruct*)byteArrayPointer).Field3 = field3;
}
}
public static void SetField4(this byte[] byteArray, byte[] field4)
{
if (field4.Length != ExampleStruct.CField4Length)
throw new ArgumentException("Byte array must have length 18", "field4");
fixed (byte* byteArrayPointer = &byteArray[0])
{
for (int i = 0; i < ExampleStruct.CField4Length; i++)
(*(ExampleStruct*)byteArrayPointer).Field4[i] = field4[i];
}
}
}
class TestProgram
{
byte[] exampleData =
{
// These 8 bytes should go in 'field1'
0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,
// These 4 bytes should go in 'field2'
0x08,0x09,0x0A,0x0B,
// These 2 bytes should go in 'field3'
0x0C,0x0D,
// These 18 * 1 bytes should go in 'field4'
0x0E,0x0F,0x10,0x11,0x12,0x13,0x14,0x15,0x16,0x17,0x18,0x19,0x1A,0x1B,0x1C,0x1D,0x1E,0x1F,
};
public void TestMethod()
{
UInt64 field1 = exampleData.GetField1();
UInt32 field2 = exampleData.GetField2();
UInt16 field3 = exampleData.GetField3();
byte[] field4 = exampleData.GetField4();
exampleData.SetField1(++field1);
exampleData.SetField2(++field2);
exampleData.SetField3(++field3);
exampleData.SetField4(new byte[ExampleStruct.CField4Length]
{ 0x42,0x42,0x42,0x42,0x42,0x42,0x42,0x42,0x42,0x42,0x42,0x42,0x42,0x42,0x42,0x42,0x42,0x42 });
}
}
}
#6
0
To use classes instead of struct, you can use something like the following. I used various elements of the above answers and put it together for a high performance class. If you don't like all the manual coding of the Serialize/Deserialize methods, you could quite easily write a code generator that would iterate through all the fields/properties and emit the appropriate methods. Here is the code:
要使用类而不是struct,可以使用类似下面的内容。我使用了上述答案的各种元素,并将它们组合在一起以形成高性能课程。如果您不喜欢Serialize / Deserialize方法的所有手动编码,您可以非常轻松地编写代码生成器,该代码生成器将遍历所有字段/属性并发出适当的方法。这是代码:
public interface ISerializableClass
{
int SerializableClassSize { get; }
StreamInflateTest Deserialize(byte[] buffer, int startOffset);
byte[] Serialize(byte[] buffer, int startOffset);
}
public class StreamInflateTest : ISerializableClass
{
private const int _classSize = 10;
public float Float32Value { get; set; }
public Int32 Int32Value { get; set; }
public byte Byte8Value { get; set; }
public bool IsOk0 { get; set; }
public bool IsOk1 { get; set; }
public bool IsOk2 { get; set; }
public bool IsOk3 { get; set; }
public bool IsOk4 { get; set; }
public StreamInflateTest()
{
}
public int SerializableClassSize { get { return _classSize; } }
public StreamInflateTest(byte[] buffer, int startOffset)
{
Deserialize(buffer, startOffset);
}
public unsafe StreamInflateTest Deserialize(byte[] buffer, int startOffset)
{
fixed (byte* pb = &buffer[startOffset])
{
Float32Value = *(float*)pb;
Int32Value = *(int*)(pb + 4);
Byte8Value = pb[8];
BitField8 bitfld = new BitField8(pb[9]);
IsOk0 = bitfld.Bit0;
IsOk1 = bitfld.Bit1;
IsOk2 = bitfld.Bit2;
IsOk3 = bitfld.Bit3;
IsOk4 = bitfld.Bit4;
}
return this;
}
public unsafe byte[] Serialize(byte[] buffer, int startOffset)
{
fixed (byte* pb = &buffer[startOffset])
{
*(float*)pb = Float32Value;
*(int*)(pb + 4) = Int32Value;
pb[8] = Byte8Value;
BitField8 bitfld = new BitField8(0)
{
Bit0 = IsOk0,
Bit1 = IsOk1,
Bit2 = IsOk2,
Bit3 = IsOk3,
Bit4 = IsOk4
};
pb[9] = bitfld.Value;
}
return buffer;
}
}
public struct BitField8
{
public byte Value;
public BitField8(byte value)
{
Value = value;
}
public bool Bit0
{
get { return (Value & 0x01) != 0; }
set
{
if (value)
Value |= 0x01;
else
Value = (byte)(Value & 0xFE); // clear the bit
}
}
public bool Bit1
{
get { return (Value & 0x02) != 0; }
set
{
if (value)
Value |= 0x02;
else
Value = (byte)(Value & 0xFD); // clear the bit
}
}
public bool Bit2
{
get { return (Value & 0x04) != 0; }
set
{
if (value)
Value |= 0x04;
else
Value = (byte)(Value & 0xFB); // clear the bit
}
}
public bool Bit3
{
get { return (Value & 0x08) != 0; }
set
{
if (value)
Value |= 0x08;
else
Value = (byte)(Value & 0xF7); // clear the bit
}
}
public bool Bit4
{
get { return (Value & 0x10) != 0; }
set
{
if (value)
Value |= 0x10;
else
Value = (byte)(Value & 0xEF); // clear the bit
}
}
public bool Bit5
{
get { return (Value & 0x20) != 0; }
set
{
if (value)
Value |= 0x20;
else
Value = (byte)(Value & 0xDF); // clear the bit
}
}
public bool Bit6
{
get { return (Value & 0x40) != 0; }
set
{
if (value)
Value |= 0x40;
else
Value = (byte)(Value & 0xBF); // clear the bit
}
}
public bool Bit7
{
get { return (Value & 0x80) != 0; }
set
{
if (value)
Value |= 0x80;
else
Value = (byte)(Value & 0x7F); // clear the bit
}
}
public bool Set(bool value, int bitNo)
{
if (bitNo > 7 || bitNo < 0)
throw new ArgumentOutOfRangeException();
if (value)
Value |= (byte)(0x01 << bitNo);
else
Value = (byte)(Value & ~(0x01 << bitNo)); // clear the bit
return value;
}
public bool Get(int bitNo)
{
if (bitNo > 7 || bitNo < 0)
throw new ArgumentOutOfRangeException();
return ((Value >> bitNo) & 0x01) != 0;
}
public bool this[int bitNo]
{
get { return Get(bitNo); }
set { Set(value, bitNo); }
}
}
#7
0
This can be used to marshal a byte array and rotate the byte order. Handy for network messages passed up from C. Wrap your structs in a class to pass them by ref.
这可用于编组字节数组并旋转字节顺序。方便从C传递的网络消息。将您的结构包装在类中以通过ref传递它们。
using System;
using System.Runtime.InteropServices;
using System.Reflection;
using System.Net;
namespace ConsoleApp1
{
[StructLayout(LayoutKind.Sequential, Pack = 1)]
struct MarshalMe
{
private UInt16 _param1;
private UInt32 _param2;
private UInt16 _param3;
private UInt16 _param4;
public ushort Param1 { get => _param1; }
public uint Param2 { get => _param2; }
public ushort Param3 { get => _param3; }
public ushort Param4 { get => _param4; }
}
class Program
{
static void Main(string[] args)
{
byte[] bytes = new byte[] {0x00, 0x03, 0x00, 0x00, 0x00, 0x04, 0x00, 0x05, 0x00, 0x00 };
var metoo = rotateStruct<MarshalMe>(stamp<MarshalMe>(bytes));
Console.WriteLine("{0}-{1}-{2}", metoo.Param1, metoo.Param2, metoo.Param3);
Console.ReadKey();
}
private static T stamp<T>(byte[] bytes)
{
var handle = GCHandle.Alloc(bytes, GCHandleType.Pinned);
var structure = Marshal.PtrToStructure(handle.AddrOfPinnedObject(), typeof(T));
handle.Free();
return (T)structure;
}
private static T rotateStruct<T>(object value)
{
FieldInfo[] myFieldInfo;
Type myType = typeof(T);
// Get the type and fields of FieldInfoClass.
myFieldInfo = myType.GetFields(BindingFlags.NonPublic | BindingFlags.Instance);
foreach (var s in myFieldInfo)
{
if (s.FieldType.Name == "UInt16"){
UInt16 u16 = (ushort)s.GetValue(value);
u16 = (ushort)IPAddress.HostToNetworkOrder((short)u16);
s.SetValue(value,u16 );
}
else if(s.FieldType.Name == "UInt32")
{
UInt32 u32 = (uint)s.GetValue(value);
u32 = (uint)IPAddress.HostToNetworkOrder((int)u32);
s.SetValue(value, (object)u32);
}
else if (s.FieldType.Name == "UInt64")
{
UInt64 u64 = (ushort)s.GetValue(value);
u64 = (ulong)IPAddress.HostToNetworkOrder((long)u64);
s.SetValue(value, u64);
}
}
return (T)value;
}
}
}
#1
13
You have a lot of options, the one that turns out the best usually depends on what your program needs/can handle and exactly how much speed you want. There are a lot of articles that explain the different ways you can populate a class or a struct with data.
你有很多选择,最好的选择通常取决于你的程序需要/可以处理什么以及你想要多少速度。有很多文章解释了使用数据填充类或结构的不同方法。
Binary Serialization is one way. That requires you write a custom serializer, which is fairly easy. An MSDN article regarding that shows it's not too difficult.
二进制序列化是一种方法。这需要您编写自定义序列化程序,这非常简单。有关这方面的MSDN文章显示它并不太难。
You can write a private constructor that takes in the byte array and uses a BinaryReader or the BitConverter class to read from the byte array (you'd have to wrap it in a MemoryStream
for BinaryReader
) or to simply convert sections of the byte array to the values you need (BitConverter
). In the case of BitConverter
you would also need to use Buffer.BlockCopy
to copy the remaining data of the byte array to the byte array field in your class
您可以编写一个私有构造函数,它接受字节数组并使用BinaryReader或BitConverter类从字节数组中读取(您必须将其包装在MemoryStream中用于BinaryReader)或简单地将字节数组的部分转换为你需要的值(BitConverter)。在BitConverter的情况下,您还需要使用Buffer.BlockCopy将字节数组的剩余数据复制到类中的字节数组字段
A third way, also generally the fastest way, is to convert your class to a struct and use unsafe code to cast the byte array as that struct. Something like this:
第三种方法,通常也是最快的方法,是将您的类转换为结构并使用不安全的代码将字节数组转换为该结构。像这样的东西:
unsafe struct ExampleClass
{
public ulong field1;
public uint field2
public ushort field3
public fixed byte field4[18];
public static ExampleClass ReadStruct(byte[] data)
{
fixed (byte* pb = &data[0])
{
return *(ExampleClass*)pb;
}
}
}
Of course, the above code is only valid if you can use unsafe code. Furthermore, converting the class to a struct may also not be what you're looking for. In most cases, structs are passed by value to functions, so that calling methods copies the entire struct (in this case 32 bytes) instead of passing a reference (4 or 8 bytes, depending on CPU architecture) and can reduce the efficiency of your program. There are exceptions to structs being passed by value, and you can use your favorite search engine for that.
当然,上述代码仅在您使用不安全代码时才有效。此外,将类转换为结构也可能不是您正在寻找的。在大多数情况下,结构体通过值传递给函数,因此调用方法复制整个结构(在这种情况下为32个字节)而不是传递引用(4或8个字节,具体取决于CPU体系结构),并且可能降低您的效率程序。结构通过值传递有例外,您可以使用自己喜欢的搜索引擎。
If you can't use unsafe code, there is also Marshal.PtrToStructure
which will do the same as the above code, but about 10 times slower. You would also need to use the MarshalAs
attribute to specify the size of the array, instead of using the fixed keyword (which is unsafe). At that point, you might as well use the BinaryReader/BitConverter, as it will be faster than the marshal class.
如果你不能使用不安全的代码,那么还有Marshal.PtrToStructure,它将与上面的代码一样,但大约慢10倍。您还需要使用MarshalAs属性来指定数组的大小,而不是使用fixed关键字(这是不安全的)。此时,您可以使用BinaryReader / BitConverter,因为它将比编组类更快。
#2
3
I think you want this:
我想你想要这个:
Here by changing the buffer in field4 you also change the other 3 which are specific types.
这里通过更改field4中的缓冲区,您还可以更改其他3个特定类型。
[StructLayout(LayoutKind.Explicit, Size=8)]
struct UValue
{
[FieldOffset(0)]
public fixed byte field4[18]; // 18 bytes long
[FieldOffset(0)]
public uint64 field1;
[FieldOffset(8)]
public Uint32 field2
[FieldOffset(12)]
public Uint16 field3
}
You might want this:
你可能想要这个:
Here by changing the buffer in fieldload you change the others as above but there is also another byte buffer at the end.
这里通过更改fieldload中的缓冲区,您可以像上面那样更改其他缓冲区,但最后还有另一个字节缓冲区。
[StructLayout(LayoutKind.Explicit, Size=8)]
struct UValue
{
[FieldOffset(0)]
public fixed byte fieldload[38]; // modify this to modify others
[FieldOffset(0)]
public uint64 field1;
[FieldOffset(8)]
public Uint32 field2
[FieldOffset(12)]
public Uint16 field3
[FieldOffset(14)]
public fixed byte field4[18]; // 18 bytes long
}
#3
3
Another option, if you can use a struct, is to Marshal the byte array directly into the structure.
如果可以使用结构,另一个选项是将字节数组直接编组到结构中。
struct ExampleStruct
{
public UInt64 field1;
public UInt32 field2;
public UInt16 field3;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 18)]
public byte[] field4;
}
And to get a struct for that:
并获得一个结构:
var handle = GCHandle.Alloc(exampleData, GCHandleType.Pinned);
var structure = Marshal.PtrToStructure(handle.AddrOfPinnedObject(), typeof (ExampleStruct));
handle.Free();
And structure
will contain your data.
结构将包含您的数据。
#4
1
Forget the efficiency thing and make your code maintainable and readable first. If necessary, profile and improve.
忘记效率的事情,让你的代码首先可维护和可读。如有必要,进行剖析和改进。
Whats wrong with using the BitConverter class.
使用BitConverter类有什么问题。
field1 = BitConverter.ToInt64(exampleData, 0)
field2 = BitConverter.ToInt32(exampleData, 8)
field3 = BitConverter.ToInt16(exampleData, 12)
Array.Copy(exampleData, 14, field4, 0, 18)
#5
1
And now for something completely different ...
现在换一些完全不同的东西......
This does not really answer the OP's question; instead it is something I cooked up to provide a way of mapping a C# struct on top of a byte array, and allow the individual fields in the underlying byte array to be both read and written. It uses unsafe code, and gets a pointer to the byte array and then casts it to a pointer to the struct that maps the fields.
这并没有真正回答OP的问题;相反,它是我做的事情,提供了一种在字节数组之上映射C#结构的方法,并允许读取和写入底层字节数组中的各个字段。它使用不安全的代码,并获取指向字节数组的指针,然后将其转换为指向映射字段的结构的指针。
This may not be all that efficient, but has the advantage of providing a symbolic mapping of the fields without the use of "magic numbers" for the lengths and offsets of the fields. But note that this will not work for data interchange with a system that uses big endian instead of little endian data representation.
这可能不是那么有效,但具有提供字段的符号映射而不使用字段的长度和偏移的“幻数”的优点。但请注意,这对于使用大端而不是小端数据表示的系统进行数据交换不起作用。
The field access methods are implemented as extension methods. See TestMethod() for an example of how to use them.
字段访问方法实现为扩展方法。有关如何使用它们的示例,请参阅TestMethod()。
This was inspired by the answer by Christopher Currens - if you find this useful please give him an upvote.
这是受到Christopher Currens的回答的启发 - 如果你觉得这很有用,请给他一个upvote。
using System;
using System.Runtime.InteropServices;
namespace ConsoleApplication1
{
[StructLayout(LayoutKind.Sequential, Pack = 1)]
unsafe struct ExampleStruct
{
internal const int CField4Length = 18;
public UInt64 Field1;
public UInt32 Field2;
public UInt16 Field3;
public fixed byte Field4[CField4Length];
}
static unsafe class ExampleStructExtensionMethods
{
public static UInt64 GetField1(this byte[] byteArray)
{
fixed (byte* byteArrayPointer = &byteArray[0])
{
return (*(ExampleStruct*)byteArrayPointer).Field1;
}
}
public static UInt32 GetField2(this byte[] byteArray)
{
fixed (byte* byteArrayPointer = &byteArray[0])
{
return (*(ExampleStruct*)byteArrayPointer).Field2;
}
}
public static UInt16 GetField3(this byte[] byteArray)
{
fixed (byte* byteArrayPointer = &byteArray[0])
{
return (*(ExampleStruct*)byteArrayPointer).Field3;
}
}
public static byte[] GetField4(this byte[] byteArray)
{
fixed (byte* byteArrayPointer = &byteArray[0])
{
byte[] field4 = new byte[ExampleStruct.CField4Length];
for (int i = 0; i < ExampleStruct.CField4Length; i++)
field4[i] = (*(ExampleStruct*)byteArrayPointer).Field4[i];
return field4;
}
}
public static void SetField1(this byte[] byteArray, UInt64 field1)
{
fixed (byte* byteArrayPointer = &byteArray[0])
{
(*(ExampleStruct*)byteArrayPointer).Field1 = field1;
}
}
public static void SetField2(this byte[] byteArray, UInt32 field2)
{
fixed (byte* byteArrayPointer = &byteArray[0])
{
(*(ExampleStruct*)byteArrayPointer).Field2 = field2;
}
}
public static void SetField3(this byte[] byteArray, UInt16 field3)
{
fixed (byte* byteArrayPointer = &byteArray[0])
{
(*(ExampleStruct*)byteArrayPointer).Field3 = field3;
}
}
public static void SetField4(this byte[] byteArray, byte[] field4)
{
if (field4.Length != ExampleStruct.CField4Length)
throw new ArgumentException("Byte array must have length 18", "field4");
fixed (byte* byteArrayPointer = &byteArray[0])
{
for (int i = 0; i < ExampleStruct.CField4Length; i++)
(*(ExampleStruct*)byteArrayPointer).Field4[i] = field4[i];
}
}
}
class TestProgram
{
byte[] exampleData =
{
// These 8 bytes should go in 'field1'
0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,
// These 4 bytes should go in 'field2'
0x08,0x09,0x0A,0x0B,
// These 2 bytes should go in 'field3'
0x0C,0x0D,
// These 18 * 1 bytes should go in 'field4'
0x0E,0x0F,0x10,0x11,0x12,0x13,0x14,0x15,0x16,0x17,0x18,0x19,0x1A,0x1B,0x1C,0x1D,0x1E,0x1F,
};
public void TestMethod()
{
UInt64 field1 = exampleData.GetField1();
UInt32 field2 = exampleData.GetField2();
UInt16 field3 = exampleData.GetField3();
byte[] field4 = exampleData.GetField4();
exampleData.SetField1(++field1);
exampleData.SetField2(++field2);
exampleData.SetField3(++field3);
exampleData.SetField4(new byte[ExampleStruct.CField4Length]
{ 0x42,0x42,0x42,0x42,0x42,0x42,0x42,0x42,0x42,0x42,0x42,0x42,0x42,0x42,0x42,0x42,0x42,0x42 });
}
}
}
#6
0
To use classes instead of struct, you can use something like the following. I used various elements of the above answers and put it together for a high performance class. If you don't like all the manual coding of the Serialize/Deserialize methods, you could quite easily write a code generator that would iterate through all the fields/properties and emit the appropriate methods. Here is the code:
要使用类而不是struct,可以使用类似下面的内容。我使用了上述答案的各种元素,并将它们组合在一起以形成高性能课程。如果您不喜欢Serialize / Deserialize方法的所有手动编码,您可以非常轻松地编写代码生成器,该代码生成器将遍历所有字段/属性并发出适当的方法。这是代码:
public interface ISerializableClass
{
int SerializableClassSize { get; }
StreamInflateTest Deserialize(byte[] buffer, int startOffset);
byte[] Serialize(byte[] buffer, int startOffset);
}
public class StreamInflateTest : ISerializableClass
{
private const int _classSize = 10;
public float Float32Value { get; set; }
public Int32 Int32Value { get; set; }
public byte Byte8Value { get; set; }
public bool IsOk0 { get; set; }
public bool IsOk1 { get; set; }
public bool IsOk2 { get; set; }
public bool IsOk3 { get; set; }
public bool IsOk4 { get; set; }
public StreamInflateTest()
{
}
public int SerializableClassSize { get { return _classSize; } }
public StreamInflateTest(byte[] buffer, int startOffset)
{
Deserialize(buffer, startOffset);
}
public unsafe StreamInflateTest Deserialize(byte[] buffer, int startOffset)
{
fixed (byte* pb = &buffer[startOffset])
{
Float32Value = *(float*)pb;
Int32Value = *(int*)(pb + 4);
Byte8Value = pb[8];
BitField8 bitfld = new BitField8(pb[9]);
IsOk0 = bitfld.Bit0;
IsOk1 = bitfld.Bit1;
IsOk2 = bitfld.Bit2;
IsOk3 = bitfld.Bit3;
IsOk4 = bitfld.Bit4;
}
return this;
}
public unsafe byte[] Serialize(byte[] buffer, int startOffset)
{
fixed (byte* pb = &buffer[startOffset])
{
*(float*)pb = Float32Value;
*(int*)(pb + 4) = Int32Value;
pb[8] = Byte8Value;
BitField8 bitfld = new BitField8(0)
{
Bit0 = IsOk0,
Bit1 = IsOk1,
Bit2 = IsOk2,
Bit3 = IsOk3,
Bit4 = IsOk4
};
pb[9] = bitfld.Value;
}
return buffer;
}
}
public struct BitField8
{
public byte Value;
public BitField8(byte value)
{
Value = value;
}
public bool Bit0
{
get { return (Value & 0x01) != 0; }
set
{
if (value)
Value |= 0x01;
else
Value = (byte)(Value & 0xFE); // clear the bit
}
}
public bool Bit1
{
get { return (Value & 0x02) != 0; }
set
{
if (value)
Value |= 0x02;
else
Value = (byte)(Value & 0xFD); // clear the bit
}
}
public bool Bit2
{
get { return (Value & 0x04) != 0; }
set
{
if (value)
Value |= 0x04;
else
Value = (byte)(Value & 0xFB); // clear the bit
}
}
public bool Bit3
{
get { return (Value & 0x08) != 0; }
set
{
if (value)
Value |= 0x08;
else
Value = (byte)(Value & 0xF7); // clear the bit
}
}
public bool Bit4
{
get { return (Value & 0x10) != 0; }
set
{
if (value)
Value |= 0x10;
else
Value = (byte)(Value & 0xEF); // clear the bit
}
}
public bool Bit5
{
get { return (Value & 0x20) != 0; }
set
{
if (value)
Value |= 0x20;
else
Value = (byte)(Value & 0xDF); // clear the bit
}
}
public bool Bit6
{
get { return (Value & 0x40) != 0; }
set
{
if (value)
Value |= 0x40;
else
Value = (byte)(Value & 0xBF); // clear the bit
}
}
public bool Bit7
{
get { return (Value & 0x80) != 0; }
set
{
if (value)
Value |= 0x80;
else
Value = (byte)(Value & 0x7F); // clear the bit
}
}
public bool Set(bool value, int bitNo)
{
if (bitNo > 7 || bitNo < 0)
throw new ArgumentOutOfRangeException();
if (value)
Value |= (byte)(0x01 << bitNo);
else
Value = (byte)(Value & ~(0x01 << bitNo)); // clear the bit
return value;
}
public bool Get(int bitNo)
{
if (bitNo > 7 || bitNo < 0)
throw new ArgumentOutOfRangeException();
return ((Value >> bitNo) & 0x01) != 0;
}
public bool this[int bitNo]
{
get { return Get(bitNo); }
set { Set(value, bitNo); }
}
}
#7
0
This can be used to marshal a byte array and rotate the byte order. Handy for network messages passed up from C. Wrap your structs in a class to pass them by ref.
这可用于编组字节数组并旋转字节顺序。方便从C传递的网络消息。将您的结构包装在类中以通过ref传递它们。
using System;
using System.Runtime.InteropServices;
using System.Reflection;
using System.Net;
namespace ConsoleApp1
{
[StructLayout(LayoutKind.Sequential, Pack = 1)]
struct MarshalMe
{
private UInt16 _param1;
private UInt32 _param2;
private UInt16 _param3;
private UInt16 _param4;
public ushort Param1 { get => _param1; }
public uint Param2 { get => _param2; }
public ushort Param3 { get => _param3; }
public ushort Param4 { get => _param4; }
}
class Program
{
static void Main(string[] args)
{
byte[] bytes = new byte[] {0x00, 0x03, 0x00, 0x00, 0x00, 0x04, 0x00, 0x05, 0x00, 0x00 };
var metoo = rotateStruct<MarshalMe>(stamp<MarshalMe>(bytes));
Console.WriteLine("{0}-{1}-{2}", metoo.Param1, metoo.Param2, metoo.Param3);
Console.ReadKey();
}
private static T stamp<T>(byte[] bytes)
{
var handle = GCHandle.Alloc(bytes, GCHandleType.Pinned);
var structure = Marshal.PtrToStructure(handle.AddrOfPinnedObject(), typeof(T));
handle.Free();
return (T)structure;
}
private static T rotateStruct<T>(object value)
{
FieldInfo[] myFieldInfo;
Type myType = typeof(T);
// Get the type and fields of FieldInfoClass.
myFieldInfo = myType.GetFields(BindingFlags.NonPublic | BindingFlags.Instance);
foreach (var s in myFieldInfo)
{
if (s.FieldType.Name == "UInt16"){
UInt16 u16 = (ushort)s.GetValue(value);
u16 = (ushort)IPAddress.HostToNetworkOrder((short)u16);
s.SetValue(value,u16 );
}
else if(s.FieldType.Name == "UInt32")
{
UInt32 u32 = (uint)s.GetValue(value);
u32 = (uint)IPAddress.HostToNetworkOrder((int)u32);
s.SetValue(value, (object)u32);
}
else if (s.FieldType.Name == "UInt64")
{
UInt64 u64 = (ushort)s.GetValue(value);
u64 = (ulong)IPAddress.HostToNetworkOrder((long)u64);
s.SetValue(value, u64);
}
}
return (T)value;
}
}
}