[Примечание: этот вопрос имел исходное название " объединение стилей C (ish) в С#" но, как сказал мне отчет Джеффа, видимо, эта структура называется "дискриминационным союзом".
Извините многословие этого вопроса.
Есть несколько аналогичных звуковых вопросов для моего уже в SO, но они, похоже, концентрируются на преимуществах экономии памяти в союзе или использовании их для взаимодействия. Вот пример такого вопроса.
Мое желание иметь тип типа объединения несколько отличается.
Я пишу код в данный момент, который генерирует объекты, которые немного похожи на это
public class ValueWrapper
{
public DateTime ValueCreationDate;
// ... other meta data about the value
public object ValueA;
public object ValueB;
}
Довольно сложный материал, я думаю, вы согласитесь. Дело в том, что ValueA
может быть только нескольких определенных типов (скажем, string
, int
и Foo
(который является классом), а ValueB
может быть другим небольшим набором типов. t нравится рассматривать эти значения как объекты (я хочу, чтобы теплое плотное чувство кодирования с некоторой степенью безопасности).
Итак, я подумал о том, чтобы написать тривиальный маленький класс-оболочку, чтобы выразить тот факт, что 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;
}
что-то вроде того, чего я хотел достичь, но у меня отсутствует один довольно важный элемент - это проверка принудительного типа компилятора при вызове функций 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, если это char
, так как в его определении явно сказано, что это не так - это ошибка программирования, и я хотел бы, чтобы компилятор подхватил это. [И если я смогу получить это правильно, то (надеюсь), я тоже получу intellisense - что было бы благом.]
Чтобы достичь этого, я хотел бы сказать компилятору, что тип 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;
}
Кто-нибудь знает, возможно ли то, что я хочу достичь? Или я просто глупый для написания этого класса в первую очередь?
Спасибо заранее.