[Anmerkung: Diese Frage hatte den Originaltitel " C (ish) style union in C # ", aber wie Jeffs Kommentar mir mitteilte, wird diese Struktur anscheinend als "diskriminierte Union" bezeichnet.]
Entschuldigen Sie die Ausführlichkeit dieser Frage.
Es gibt einige ähnlich klingende Fragen zu meinen bereits in SO, aber sie scheinen sich auf die speichersparenden Vorteile der Gewerkschaft zu konzentrieren oder sie für Interop zu verwenden. Hier ist ein Beispiel für eine solche Frage .
Mein Wunsch nach einer gewerkschaftlichen Sache ist etwas anders.
Ich schreibe gerade Code, der Objekte generiert, die ein bisschen so aussehen
public class ValueWrapper
{
public DateTime ValueCreationDate;
// ... other meta data about the value
public object ValueA;
public object ValueB;
}
Ziemlich kompliziertes Zeug, ich denke du wirst mir zustimmen. Die Sache ist, dass ValueA
es nur wenige bestimmte Typen geben kann (sagen wir string
, int
und Foo
(was eine Klasse ist) und ValueB
eine andere kleine Gruppe von Typen sein kann. Ich mag es nicht, diese Werte als Objekte zu behandeln (ich möchte das warme, gemütliche Gefühl von Codierung mit ein wenig Typensicherheit).
Also habe ich darüber nachgedacht, eine triviale kleine Wrapper-Klasse zu schreiben, um die Tatsache auszudrücken, dass ValueA logisch eine Referenz auf einen bestimmten Typ ist. Ich rief die Klasse an, Union
weil mich das, was ich erreichen wollte, an das Gewerkschaftskonzept in C erinnerte.
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);
}
}
Die Verwendung dieser Klasse ValueWrapper sieht jetzt so aus
public class ValueWrapper2
{
public DateTime ValueCreationDate;
public Union<int, string, Foo> ValueA;
public Union<double, Bar, Foo> ValueB;
}
Das ist ungefähr das, was ich erreichen wollte, aber mir fehlt ein ziemlich wichtiges Element - die vom Compiler erzwungene Typprüfung beim Aufrufen der Is- und As-Funktionen, wie der folgende Code zeigt
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 Es ist nicht gültig, ValueA zu fragen, ob es sich um eine handelt, char
da die Definition eindeutig besagt, dass dies nicht der Fall ist. Dies ist ein Programmierfehler, und ich möchte, dass der Compiler dies aufgreift . [Auch wenn ich das richtig machen könnte, würde ich (hoffentlich) auch Intellisense bekommen - was ein Segen wäre.]
Um dies zu erreichen, möchte ich dem Compiler mitteilen, dass der Typ T
A, B oder C sein kann
public bool Is<T>() where T : A
or T : B // Yes I know this is not legal!
or T : C
{
return typeof(T) == type;
}
Hat jemand eine Idee, ob das, was ich erreichen möchte, möglich ist? Oder bin ich einfach nur dumm, diese Klasse überhaupt zu schreiben?
Danke im Voraus.
StructLayout(LayoutKind.Explicit)
und implementiert werdenFieldOffset
. Dies ist natürlich nicht mit Referenztypen möglich. Was Sie tun, ist überhaupt nicht wie eine C-Union.