[Catatan: Pertanyaan ini memiliki judul asli " C (ish) style union in C # " tetapi seperti yang diberitahukan oleh Jeff, struktur ini disebut 'union yang terdiskriminasi']
Maafkan verbositas pertanyaan ini.
Ada beberapa pertanyaan serupa yang sudah saya miliki di SO tetapi tampaknya berkonsentrasi pada manfaat penyimpanan memori dari union atau menggunakannya untuk interop. Berikut adalah contoh pertanyaan semacam itu .
Keinginan saya untuk memiliki jenis persatuan agak berbeda.
Saya sedang menulis beberapa kode saat ini yang menghasilkan objek yang terlihat seperti ini
public class ValueWrapper
{
public DateTime ValueCreationDate;
// ... other meta data about the value
public object ValueA;
public object ValueB;
}
Hal-hal yang cukup rumit, saya pikir Anda akan setuju. Masalahnya adalah itu ValueA
hanya bisa dari beberapa jenis tertentu (katakanlah string
, int
dan Foo
(yang merupakan kelas) dan ValueB
bisa menjadi kumpulan jenis kecil lainnya. Saya tidak suka memperlakukan nilai-nilai ini sebagai objek (saya ingin perasaan hangat pas coding dengan sedikit keamanan tipe).
Jadi saya berpikir untuk menulis kelas pembungkus kecil yang sepele untuk mengungkapkan fakta bahwa ValueA secara logis adalah referensi ke tipe tertentu. Saya menelepon kelas Union
karena apa yang saya coba capai mengingatkan saya pada konsep penyatuan di C.
public class Union<A, B, C>
{
private readonly Type type;
public readonly A a;
public readonly B b;
public readonly C c;
public A A{get {return a;}}
public B B{get {return b;}}
public C C{get {return c;}}
public Union(A a)
{
type = typeof(A);
this.a = a;
}
public Union(B b)
{
type = typeof(B);
this.b = b;
}
public Union(C c)
{
type = typeof(C);
this.c = c;
}
/// <summary>
/// Returns true if the union contains a value of type T
/// </summary>
/// <remarks>The type of T must exactly match the type</remarks>
public bool Is<T>()
{
return typeof(T) == type;
}
/// <summary>
/// Returns the union value cast to the given type.
/// </summary>
/// <remarks>If the type of T does not exactly match either X or Y, then the value <c>default(T)</c> is returned.</remarks>
public T As<T>()
{
if(Is<A>())
{
return (T)(object)a; // Is this boxing and unboxing unavoidable if I want the union to hold value types and reference types?
//return (T)x; // This will not compile: Error = "Cannot cast expression of type 'X' to 'T'."
}
if(Is<B>())
{
return (T)(object)b;
}
if(Is<C>())
{
return (T)(object)c;
}
return default(T);
}
}
Menggunakan kelas ini ValueWrapper sekarang terlihat seperti ini
public class ValueWrapper2
{
public DateTime ValueCreationDate;
public Union<int, string, Foo> ValueA;
public Union<double, Bar, Foo> ValueB;
}
yang seperti apa yang ingin saya capai tetapi saya kehilangan satu elemen yang cukup penting - yaitu pemeriksaan tipe yang dipaksakan oleh compiler saat memanggil fungsi Is dan As seperti yang ditunjukkan kode berikut
public void DoSomething()
{
if(ValueA.Is<string>())
{
var s = ValueA.As<string>();
// .... do somethng
}
if(ValueA.Is<char>()) // I would really like this to be a compile error
{
char c = ValueA.As<char>();
}
}
IMO Tidak sah untuk menanyakan ValueA apakah itu a char
karena definisinya dengan jelas mengatakan bukan - ini adalah kesalahan pemrograman dan saya ingin kompilator memahami ini. [Juga jika saya bisa mendapatkan ini dengan benar maka (semoga) saya akan mendapatkan kecerdasan juga - yang akan menjadi keuntungan.]
Untuk mencapai ini, saya ingin memberi tahu kompiler bahwa jenisnya T
bisa salah satu dari A, B atau C
public bool Is<T>() where T : A
or T : B // Yes I know this is not legal!
or T : C
{
return typeof(T) == type;
}
Adakah yang tahu apakah yang ingin saya capai itu mungkin? Atau apakah saya hanya bodoh karena menulis kelas ini sejak awal?
Terima kasih sebelumnya.
StructLayout(LayoutKind.Explicit)
danFieldOffset
. Ini tidak dapat dilakukan dengan tipe referensi, tentu saja. Apa yang Anda lakukan sama sekali tidak seperti C Union.