Hay veces que uno no encuentra lo que busca, y cuando no lo está buscando, lo encuentra. O algo así. El hecho está en que empecé a hacer una pequeña utilidad interna en C# que necesitaba bastantes estructuras y encima con campos repetidos.
Eso en C++ es fácil de solventar. Un se crea una jerarquía de estructuras igual que podría hacerlo de clases, y listo. Es decir, si yo necesito tres estructuras diferentes que tengan, por ejemplo, varios campos del mismo nombre y tipo, lo más fácil es crear una estructura base y heredar de ella. Algo así:
struct Base
{
string Nombre;
string NombreAlternativo;
};
struct Casa:public Base
{
string Dirección;
};
struct Coche:public Base
{
string Propietario;
};
De este modo ahorro escritura, ya que tanto la estructura Casa como Coche tendrán los campos Nombre y NombreAlternativo sin haberlos tenido que teclear. Suena un poco trivial pero cuando tienes diez o doce estructuras lo agradeces, incluso de esta forma te evitas los errores de tecleo y pones en una de ellas un campo con “Nonbre” en lugar de Nombre.
Eso en C++, en C# llega uno tan contento, se pone a picar código y… ¡ñas, no deja! El compilador protesta como un loco…
Me voy a la ayuda y, efectivamente, una limitación más del .NET: pese a que ellas mismas heredan de ValueType e indirectamente de Object (por cierto realizando una metástasis muy extraña y pasando de ser un tipo-referencia, como es Object, a un tipo-valor…)
Pero sigo leyendo para abajo y me encuentro con una sorpresa, y es cómo hacer uniones aprovechando los atributos.
El tema está aquí, y básicamente viene a decir, en la sección “Attributes on Structs”, que utilizando los atributos “LayoutKind.Explicit” y “FieldOffset”, podemos definir el punto de inicio de cada elemento de la estructura de forma manual.
Veamos un ejemplo muy socorrido en C/C++ en relación a las direcciones IP, que todos hemos manejado alguna vez. Una dirección IP se puede ver como un entero sin signo de 32 bits (que es como las ve el software) o como cuatro números situados entre 0 y 255. Esto en C/C++ se puede expresar así:
union IP
{
__int32 As32bits;
unsigned char asBytes[4];
};
Para referirnos a ellas, podemos usar ambas partes y ambas serán el mismo valor:
IP miIP;
miIp.AsBytes[0]=192;
miIp.AsBytes[0]=168;
miIp.AsBytes[0]=1;
miIp.AsBytes[0]=1;
Y como miIP.As32bits tendremos en valor equivalente.
Pues bien, en C# esto se puede conseguir más o menos así:
using System.Runtime.InteropServices;
[StructLayour(LayoutKind.Explicit)]
struct IP
{
[FieldOffset(0)]
Public Int32 As32bits;
[FieldOffset(0)]
Public byte a;
[FieldOffset(1)]
Public byte b;
[FieldOffset(2)]
Public byte c;
[FieldOffset(3)]
Public byte d;
}
Y podríamos acceder a ella tanto asignando los valores a, b, c, d como As32bits. También podríamos añadirlo como un array, pero eso queda de ejercicio de clase.
(NOTA: La entrada está escrita sin comprobar el código, por lo que podría haber algún gazapo. De todos modos, la esencia del código es correcta.)