[注: この質問の元のタイトルは「C# の C (ish) スタイル ユニオン」でしたが、Jeff のコメントからわかるように、この構造は「差別化されたユニオン」と呼ばれているようです]
この質問の冗長さを許してください。
すでに SO に似たような質問がいくつかありますが、それらはユニオンのメモリ節約の利点または相互運用のためにそれを使用することに集中しているようです。 そのような質問の例を次に示します。
ユニオンタイプのものを持ちたいという私の願望は、多少異なります。
現在、このようなオブジェクトを生成するコードを書いています。
public class ValueWrapper
{
public DateTime ValueCreationDate;
// ... other meta data about the value
public object ValueA;
public object ValueB;
}
かなり複雑なことですが、同意していただけると思います。問題は、それはいくつかの特定の型 (例えば, and (クラス) としValueA
ましょう) だけであり、別の小さな型のセットになる可能性があるということです。私はこれらの値をオブジェクトとして扱うのは好きではありません (私は、型安全性を考慮したコーディング)。 string
int
Foo
ValueB
そこで、ValueA が論理的に特定の型への参照であるという事実を表現するために、単純な小さなラッパー クラスを作成することを考えました。Union
私が達成しようとしていることが 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);
}
}
このクラスを使用すると ValueWrapper は次のようになります
public class ValueWrapper2
{
public DateTime ValueCreationDate;
public Union<int, string, Foo> ValueA;
public Union<double, Bar, Foo> ValueB;
}
これは私が達成したかったことのようなものですが、かなり重要な要素が 1 つ欠けています。それは、次のコードが示すように、Is および As 関数を呼び出すときにコンパイラが強制する型チェックです。
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 ValueA が a であるかどうかを尋ねるのは有効ではありません。char
その定義が明確にそうではないことを示しているためです。これはプログラミング エラーであり、コンパイラにこれを取り上げてもらいたいと思います。[また、これを正しく理解できれば、(願わくば) インテリセンスも取得できます。これは恩恵です。]
T
これを実現するために、型が A、B、または 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;
}
私が達成したいことが可能かどうか、誰にも分かりますか? それとも、そもそもこのクラスを書いた私は単純に愚かでしょうか?
前もって感謝します。