[Примечание: этот вопрос имел первоначальное название « Объединение в стиле C (ish) в C # », но, как мне сообщил комментарий Джеффа, очевидно, эта структура называется «размеченным объединением»]
Извините за многословность этого вопроса.
Есть несколько похожих вопросов, которые я могу задать в SO, но они, похоже, сосредоточены на преимуществах объединения памяти или использовании его для взаимодействия. Вот пример такого вопроса .
Мое желание иметь что-то типа профсоюзов несколько иное.
В данный момент я пишу код, который генерирует объекты, похожие на этот
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<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, является ли это a, 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;
}
Кто-нибудь знает, возможно ли то, чего я хочу достичь? Или я просто глуп, что вообще написал этот класс?
Заранее спасибо.
StructLayout(LayoutKind.Explicit)
иFieldOffset
. Конечно, этого нельзя сделать со ссылочными типами. То, что вы делаете, совсем не похоже на C Union.