Размеченное объединение в C#



[Примечание: этот вопрос имел оригинальное название "объединение стиля 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>();
}
}


ИМО это не допустимо, чтобы задать Значение_а, если это char поскольку его определение четко говорит, что это не так - это ошибка программирования, и я хотел бы, чтобы компилятор, чтобы поднять на этом. [Также, если бы я мог получить это правильно, то (надеюсь) я бы тоже получил intellisense - что было бы благом.]



In чтобы добиться этого, я хотел бы сказать компилятору, что тип 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;
}


есть ли у кого-нибудь идеи, если то, что я хочу достичь возможно? Или я просто тупой для написания этого класса в первую очередь?



спасибо заранее.

379   0  

Comments

    Ничего не найдено.