C# Language
Hoe C # Structs te gebruiken om een Union-type te maken (vergelijkbaar met C Unions)
Zoeken…
Opmerkingen
Union-typen worden in verschillende talen, met name C-taal, gebruikt om verschillende typen te bevatten die in dezelfde geheugenruimte kunnen "overlappen". Met andere woorden, ze kunnen verschillende velden bevatten die allemaal beginnen met dezelfde geheugenoffset, zelfs als ze verschillende lengtes en typen kunnen hebben. Dit heeft het voordeel dat het zowel geheugen bespaart als automatische conversie uitvoert.
Let op de opmerkingen in de constructor van de Struct. De volgorde waarin de velden worden geïnitialiseerd is uiterst belangrijk. U wilt eerst alle andere velden initialiseren en vervolgens de waarde instellen die u wilt wijzigen als de laatste instructie. Omdat de velden elkaar overlappen, is de laatste waarde-instelling degene die telt.
C-stijl vakbonden in C #
Union-typen worden in verschillende talen, zoals C-taal, gebruikt om verschillende typen te bevatten die kunnen "overlappen". Met andere woorden, ze kunnen verschillende velden bevatten die allemaal beginnen met dezelfde geheugenoffset, zelfs als ze verschillende lengtes en typen kunnen hebben. Dit heeft het voordeel dat het zowel geheugen bespaart als automatische conversie uitvoert. Denk bijvoorbeeld aan een IP-adres. Intern wordt een IP-adres weergegeven als een geheel getal, maar soms willen we toegang krijgen tot de verschillende Byte-component, zoals in Byte1.Byte2.Byte3.Byte4. Dit werkt voor alle waardetypen, of het nu primitieven zijn zoals Int32 of lang, of voor andere structs die u zelf definieert.
We kunnen hetzelfde effect bereiken in C # door Expliciete layoutstructuren te gebruiken.
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}";
}
Door Struct op deze manier te definiëren, kunnen we het gebruiken zoals we een Union in C zouden gebruiken. Laten we bijvoorbeeld een IP-adres maken als een willekeurig geheel getal en vervolgens het eerste token in het adres wijzigen in '100', door het te wijzigen van 'ABCD' tot '100.BCD':
var ip = new IpAddress(new Random().Next());
Console.WriteLine($"{ip} = {ip.Address}");
ip.Byte1 = 100;
Console.WriteLine($"{ip} = {ip.Address}");
Output:
75.49.5.32 = 537211211
100.49.5.32 = 537211236
Union Types in C # kunnen ook Struct-velden bevatten
Afgezien van primitieven, kunnen de Explicit Layout-structuren (Unions) in C # ook andere Structs bevatten. Zolang een veld een Waardetype is en geen Referentie, kan het zich in een Union bevinden:
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}/";
}
We kunnen nu controleren of de hele Service Union past in de grootte van een lange (8 bytes).
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}.");