C# Language
Jak używać struktur C # do tworzenia typu Unii (podobnego do związków C)
Szukaj…
Uwagi
Typy Unii są używane w kilku językach, zwłaszcza w języku C, aby zawierać kilka różnych typów, które mogą „nakładać się” w tej samej przestrzeni pamięci. Innymi słowy, mogą zawierać różne pola, z których wszystkie zaczynają się od tego samego przesunięcia pamięci, nawet jeśli mogą mieć różne długości i typy. Ma to tę zaletę, że oszczędza pamięć i wykonuje automatyczną konwersję.
Zwróć uwagę na komentarze w konstruktorze Struct. Kolejność inicjowania pól jest niezwykle ważna. Chcesz najpierw zainicjować wszystkie pozostałe pola, a następnie ustawić wartość, którą zamierzasz zmienić jako ostatnią instrukcję. Ponieważ pola nakładają się, ostatnia konfiguracja wartości jest tą, która się liczy.
Związki w stylu C w C #
Typy Unii są używane w kilku językach, takich jak język C, aby zawierać kilka różnych typów, które mogą się „nakładać”. Innymi słowy, mogą zawierać różne pola, z których wszystkie zaczynają się od tego samego przesunięcia pamięci, nawet jeśli mogą mieć różne długości i typy. Ma to tę zaletę, że oszczędza pamięć i wykonuje automatyczną konwersję. Pomyśl o adresie IP jako przykładzie. Wewnętrznie adres IP jest reprezentowany jako liczba całkowita, ale czasami chcemy uzyskać dostęp do innego komponentu Byte, jak w Byte1.Byte2.Byte3.Byte4. Działa to dla każdego typu wartości, czy to prymitywów takich jak Int32 lub długi, czy też dla innych struktur, które sam definiujesz.
Ten sam efekt możemy osiągnąć w języku C #, używając jawnych struktur układu.
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}";
}
Po zdefiniowaniu Struct w ten sposób możemy go używać tak, jakbyśmy używali Unii w C. Na przykład, stwórzmy adres IP jako Losową liczbę całkowitą, a następnie zmodyfikuj pierwszy token w adresie na „100”, zmieniając go z „ABCD” na „100.BCD”:
var ip = new IpAddress(new Random().Next());
Console.WriteLine($"{ip} = {ip.Address}");
ip.Byte1 = 100;
Console.WriteLine($"{ip} = {ip.Address}");
Wynik:
75.49.5.32 = 537211211
100.49.5.32 = 537211236
Typy związków w C # mogą również zawierać pola Struct
Oprócz prymitywów, struktury Jawnego Układu (Związki) w C # mogą również zawierać inne Struktury. Tak długo, jak pole jest typem wartości, a nie odniesieniem, może być zawarte w Unii:
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}/";
}
Możemy teraz zweryfikować, czy cały Service Union pasuje do rozmiaru długiego (8 bajtów).
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}.");