Suche…


Bemerkungen

Union-Typen werden in mehreren Sprachen verwendet, insbesondere in der C-Sprache, um verschiedene Typen zu enthalten, die sich im selben Speicherbereich "überlappen" können. Mit anderen Worten, sie können verschiedene Felder enthalten, die alle mit dem gleichen Speicheroffset beginnen, auch wenn sie unterschiedliche Längen und Typen haben. Dies hat den Vorteil, dass Sie sowohl Speicher sparen als auch eine automatische Konvertierung durchführen.

Bitte beachten Sie die Kommentare im Konstruktor des Struct. Die Reihenfolge, in der die Felder initialisiert werden, ist äußerst wichtig. Sie möchten zunächst alle anderen Felder initialisieren und dann den Wert, den Sie ändern möchten, als letzte Anweisung festlegen. Da sich die Felder überlappen, zählt der letzte Wert, der zählt.

C-Style-Vereinigungen in C #

Union-Typen werden in mehreren Sprachen verwendet, beispielsweise in der C-Sprache, um verschiedene Typen zu enthalten, die sich "überlappen" können. Mit anderen Worten, sie können verschiedene Felder enthalten, die alle mit dem gleichen Speicheroffset beginnen, auch wenn sie unterschiedliche Längen und Typen haben. Dies hat den Vorteil, dass Sie sowohl Speicher sparen als auch eine automatische Konvertierung durchführen. Stellen Sie sich eine IP-Adresse als Beispiel vor. Intern wird eine IP-Adresse als Ganzzahl dargestellt, aber manchmal möchten wir auf die andere Byte-Komponente zugreifen, wie in Byte1.Byte2.Byte3.Byte4. Dies funktioniert für alle Werttypen, sei es Grundelemente wie Int32 oder Long oder für andere von Ihnen definierte Strukturen.

Wir können den gleichen Effekt in C # erzielen, indem Sie explizite Layoutstrukturen verwenden.

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}";
}

Nachdem wir Struct auf diese Weise definiert haben, können wir es so verwenden, wie wir eine Union in C verwenden würden. Erstellen Sie beispielsweise eine IP-Adresse als Random Integer und ändern Sie dann das erste Token in der Adresse in '100', indem Sie es ändern von 'ABCD' bis '100.BCD':

var ip = new IpAddress(new Random().Next());
Console.WriteLine($"{ip} = {ip.Address}");
ip.Byte1 = 100;
Console.WriteLine($"{ip} = {ip.Address}");

Ausgabe:

75.49.5.32 = 537211211
100.49.5.32 = 537211236

Demo anzeigen

Union-Typen in C # können auch Struct-Felder enthalten

Neben Primitiven können die Explicit Layout-Strukturen (Vereinigungen) in C # auch andere Structs enthalten. Solange ein Feld ein Werttyp und keine Referenz ist, kann es in einer Union enthalten sein:

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}/";
}

Wir können jetzt überprüfen, ob die gesamte Service Union in die Größe einer langen Länge (8 Byte) passt.

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}.");

Demo anzeigen



Modified text is an extract of the original Stack Overflow Documentation
Lizenziert unter CC BY-SA 3.0
Nicht angeschlossen an Stack Overflow