C# Language
Hur man använder C # Structs för att skapa en unionstyp (liknande C-fackföreningar)
Sök…
Anmärkningar
Unionstyper används på flera språk, särskilt C-språk, för att innehålla flera olika typer som kan "överlappa" i samma minnesutrymme. Med andra ord kan de innehålla olika fält som alla börjar på samma minnesförskjutning, även om de kan ha olika längder och typer. Detta har fördelen att både spara minne och göra automatisk konvertering.
Observera kommentarerna i konstruktören av strukturen. Ordningen i vilken fälten initieras är oerhört viktig. Du vill först initialisera alla andra fält och sedan ställa in det värde som du tänker ändra som det sista uttalandet. Eftersom fälten överlappar är den sista värdet inställningen som räknas.
C-Style fackföreningar i C #
Unionstyper används på flera språk, som C-språk, för att innehålla flera olika typer som kan "överlappa". Med andra ord kan de innehålla olika fält som alla börjar på samma minnesförskjutning, även om de kan ha olika längder och typer. Detta har fördelen att både spara minne och göra automatisk konvertering. Tänk på en IP-adress som ett exempel. Internt representeras en IP-adress som ett heltal, men ibland vill vi komma åt den olika Byte-komponenten, som i Byte1.Byte2.Byte3.Byte4. Detta fungerar för alla värdetyper, vare sig det är primitiv som Int32 eller lång, eller för andra strukturer som du definierar själv.
Vi kan uppnå samma effekt i C # genom att använda explicit Layout Structs.
using System;
using System.Runtime.InteropServices;
// The struct needs to be annotated as "Explicit Layout"
[StructLayout(LayoutKind.Explicit)]
struct IpAddress
{
// The "FieldOffset" means that this Integer starts, an offset in bytes.
// sizeof(int) 4, sizeof(byte) = 1
[FieldOffset(0)] public int Address;
[FieldOffset(0)] public byte Byte1;
[FieldOffset(1)] public byte Byte2;
[FieldOffset(2)] public byte Byte3;
[FieldOffset(3)] public byte Byte4;
public IpAddress(int address) : this()
{
// When we init the Int, the Bytes will change too.
Address = address;
}
// Now we can use the explicit layout to access the
// bytes separately, without doing any conversion.
public override string ToString() => $"{Byte1}.{Byte2}.{Byte3}.{Byte4}";
}
Efter att ha definierat strukturen på detta sätt kan vi använda den som vi skulle använda en union i C. Låt oss till exempel skapa en IP-adress som ett slumpmässigt heltal och ändra sedan det första tokenet i adressen till '100' genom att ändra den från 'ABCD' till '100.BCD':
var ip = new IpAddress(new Random().Next());
Console.WriteLine($"{ip} = {ip.Address}");
ip.Byte1 = 100;
Console.WriteLine($"{ip} = {ip.Address}");
Produktion:
75.49.5.32 = 537211211
100.49.5.32 = 537211236
Facktyper i C # kan också innehålla strukturfält
Förutom primitiven kan de explicita layoutstrukturerna (fackföreningar) i C # också innehålla andra strukturer. Så länge ett fält är en värdetyp och inte en referens, kan det ingå i en union:
using System;
using System.Runtime.InteropServices;
// The struct needs to be annotated as "Explicit Layout"
[StructLayout(LayoutKind.Explicit)]
struct IpAddress
{
// Same definition of IpAddress, from the example above
}
// Now let's see if we can fit a whole URL into a long
// Let's define a short enum to hold protocols
enum Protocol : short { Http, Https, Ftp, Sftp, Tcp }
// The Service struct will hold the Address, the Port and the Protocol
[StructLayout(LayoutKind.Explicit)]
struct Service
{
[FieldOffset(0)] public IpAddress Address;
[FieldOffset(4)] public ushort Port;
[FieldOffset(6)] public Protocol AppProtocol;
[FieldOffset(0)] public long Payload;
public Service(IpAddress address, ushort port, Protocol protocol)
{
Payload = 0;
Address = address;
Port = port;
AppProtocol = protocol;
}
public Service(long payload)
{
Address = new IpAddress(0);
Port = 80;
AppProtocol = Protocol.Http;
Payload = payload;
}
public Service Copy() => new Service(Payload);
public override string ToString() => $"{AppProtocol}//{Address}:{Port}/";
}
Vi kan nu verifiera att hela Service Union passar in i storleken på en lång (8 byte).
var ip = new IpAddress(new Random().Next());
Console.WriteLine($"Size: {Marshal.SizeOf(ip)} bytes. Value: {ip.Address} = {ip}.");
var s1 = new Service(ip, 8080, Protocol.Https);
var s2 = new Service(s1.Payload);
s2.Address.Byte1 = 100;
s2.AppProtocol = Protocol.Ftp;
Console.WriteLine($"Size: {Marshal.SizeOf(s1)} bytes. Value: {s1.Address} = {s1}.");
Console.WriteLine($"Size: {Marshal.SizeOf(s2)} bytes. Value: {s2.Address} = {s2}.");