Поиск…


замечания

Типы Union используются на нескольких языках, особенно на C-языке, для хранения нескольких разных типов, которые могут «перекрываться» в одном и том же пространстве памяти. Другими словами, они могут содержать разные поля, все из которых начинаются с одного и того же смещения памяти, даже если они могут иметь разные длины и типы. Это имеет преимущество как для экономии памяти, так и для автоматического преобразования.

Обратите внимание на комментарии в конструкторе Struct. Порядок, в котором инициализируются поля, чрезвычайно важен. Вы хотите сначала инициализировать все остальные поля, а затем установить значение, которое вы намерены изменить в качестве последнего оператора. Поскольку поля перекрываются, последняя настройка значения - та, которая учитывается.

C-Style Unions в C #

Типы Union используются на нескольких языках, таких как C-язык, для хранения нескольких разных типов, которые могут «перекрываться». Другими словами, они могут содержать разные поля, все из которых начинаются с одного и того же смещения памяти, даже если они могут иметь разные длины и типы. Это имеет преимущество как для экономии памяти, так и для автоматического преобразования. В качестве примера рассмотрим IP-адрес. Внутренне IP-адрес представлен как целое число, но иногда мы хотим получить доступ к другому байт-компоненту, как в Byte1.Byte2.Byte3.Byte4. Это работает для любых типов значений, будь то примитивы, такие как Int32 или long, или для других структур, которые вы сами определяете.

Мы можем добиться такого же эффекта в C #, используя Явные структуры компоновки.

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

Определив Struct таким образом, мы можем использовать его, поскольку мы будем использовать Union в C. Например, давайте создадим IP-адрес как Random Integer, а затем изменим первый токен в адресе «100», изменив его от «ABCD» до «100.BCD»:

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

Выход:

75.49.5.32 = 537211211
100.49.5.32 = 537211236

Посмотреть демо

Типы Union в C # также могут содержать поля Struct

Помимо примитивов, Структуры (Unions) Явного Макета в C # также могут содержать другие Структуры. Пока поле является типом значения, а не ссылкой, оно может содержаться в Союзе:

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

Теперь мы можем проверить, что весь Service Union вписывается в размер длинного (8 байтов).

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

Посмотреть демо



Modified text is an extract of the original Stack Overflow Documentation
Лицензировано согласно CC BY-SA 3.0
Не связан с Stack Overflow