Дискриминируемое объединение в C#C#

Место общения программистов C#
Anonymous
Дискриминируемое объединение в C#

Сообщение Anonymous »

[Примечание: первоначальный заголовок этого вопроса был «Объединение стиля C (ish) в C#»
но, как сообщил мне комментарий Джеффа, по-видимому, эта структура называется «дискриминируемым объединением»]
Извините за многословие этого вопроса.
У меня уже есть несколько похожих по звучанию вопросов в SO, но они, похоже, концентрируются на преимуществах объединения памяти или его использовании для interop.
Вот пример такого вопроса.
Мое желание иметь тип объединения несколько иное.
Сейчас я пишу код, который генерирует объекты, которые выглядят примерно так.

Код: Выделить всё

public class ValueWrapper
{
public DateTime ValueCreationDate;
// ... other meta data about the value

public object ValueA;
public object ValueB;
}
Думаю, вы согласитесь, довольно сложная штука. Дело в том, что ValueA может относиться только к нескольким определенным типам (скажем, string, int и Foo (который является классом), а ValueB может быть еще одним небольшим набором типов. Мне не нравится рассматривать эти значения как объекты (мне нужно теплое ощущение уюта при кодировании с некоторой безопасностью типов).
Поэтому я подумал о написании тривиального небольшого класса-оболочки, чтобы выразить тот факт, что ValueA логически является ссылку на определенный тип. Я назвал класс Union, потому что то, чего я пытаюсь достичь, напомнило мне концепцию объединения в C.

Код: Выделить всё

public class Union
{
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;
}

/// 
/// Returns true if the union contains a value of type T
/// 
/// The type of T must exactly match the type
public bool Is()
{
return typeof(T) == type;
}

/// 
/// Returns the union value cast to the given type.
/// 
/// If the type of T does not exactly match either X or Y, then the value default(T) is returned.
public T As()
{
if(Is())
{
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())
{
return (T)(object)b;
}

if(Is())
{
return (T)(object)c;
}

return default(T);
}
}
Использование этого класса ValueWrapper теперь выглядит так

Код: Выделить всё

public class ValueWrapper2
{
public DateTime ValueCreationDate;
public  Union ValueA;
public  Union ValueB;
}
это примерно то, чего я хотел достичь, но мне не хватает одного довольно важного элемента - принудительной проверки типа компилятором при вызове функций Is и As, как показано в следующем коде

Код: Выделить всё

    public void DoSomething()
{
if(ValueA.Is())
{
var s = ValueA.As();
// .... do somethng
}

if(ValueA.Is()) // I would really like this to be a compile error
{
char c = ValueA.As();
}
}
ИМО, недопустимо спрашивать ValueA, является ли это символом, поскольку его определение ясно говорит, что это не так - это ошибка программирования, и я бы хотел, чтобы компилятор это заметил. [Кроме того, если бы я мог сделать это правильно, то (надеюсь) я бы тоже получил intellisense - что было бы благом.]
Чтобы добиться этого, я хотел бы сообщить компилятору, что тип T может быть одним из A, B или C

Код: Выделить всё

    public bool Is() where T : A
or T : B // Yes I know this is not legal!
or T : C
{
return typeof(T) == type;
}
Кто-нибудь знает, возможно ли то, чего я хочу? Или я просто глуп, что вообще написал этот класс?

Вернуться в «C#»