Buscar..


Observaciones

Los tipos de unión se utilizan en varios idiomas, especialmente en lenguaje C, para contener varios tipos diferentes que pueden "superponerse" en el mismo espacio de memoria. En otras palabras, pueden contener diferentes campos, todos los cuales comienzan en el mismo desplazamiento de memoria, incluso cuando tienen diferentes longitudes y tipos. Esto tiene la ventaja de ahorrar memoria y hacer una conversión automática.

Por favor, tenga en cuenta los comentarios en el constructor de la Struct. El orden en que se inicializan los campos es extremadamente importante. Primero desea inicializar todos los otros campos y luego establecer el valor que pretende cambiar como la última declaración. Debido a que los campos se superponen, la configuración del último valor es la que cuenta.

Uniones de estilo C en C #

Los tipos de unión se utilizan en varios idiomas, como el lenguaje C, para contener varios tipos diferentes que pueden "superponerse". En otras palabras, pueden contener diferentes campos, todos los cuales comienzan en el mismo desplazamiento de memoria, incluso cuando tienen diferentes longitudes y tipos. Esto tiene la ventaja de ahorrar memoria y hacer una conversión automática. Piense en una dirección IP, como un ejemplo. Internamente, una dirección IP se representa como un entero, pero a veces queremos acceder al componente Byte diferente, como en Byte1.Byte2.Byte3.Byte4. Esto funciona para cualquier tipo de valor, ya sea primitivos como Int32 o largo, o para otras estructuras que defina usted mismo.

Podemos lograr el mismo efecto en C # usando estructuras de diseño explícitas.

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

Habiendo definido Struct de esta manera, podemos usarlo como usaríamos una Unión en C. Por ejemplo, creemos una dirección IP como un Entero Aleatorio y luego modifiquemos el primer token en la dirección a '100', al cambiarlo de 'ABCD' a '100.BCD':

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

Salida:

75.49.5.32 = 537211211
100.49.5.32 = 537211236

Ver demostración

Los tipos de unión en C # también pueden contener campos Struct

Aparte de las primitivas, las estructuras de diseño explícito (Unions) en C #, también pueden contener otras estructuras. Mientras un campo sea un tipo de valor y no una referencia, puede estar contenido en una unión:

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

Ahora podemos verificar que toda la Unión de Servicios se ajuste al tamaño de un largo (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}.");

Ver demostración



Modified text is an extract of the original Stack Overflow Documentation
Licenciado bajo CC BY-SA 3.0
No afiliado a Stack Overflow