Unión discriminada en C #

[Nota: esta pregunta tenía el título original "Unión de estilo C (ish) en C #"pero como me informó el comentario de Jeff, aparentemente esta estructura se llama 'unión discriminada']

Disculpe la verbosidad de esta pregunta.

Ya hay un par de preguntas similares a las mías en SO, pero parecen concentrarse en los beneficios de ahorro de memoria de la unión o en su uso para interoperabilidad.Aquí hay un ejemplo de tal pregunta.

Mi deseo de tener un tipo de unión es algo diferente.

Estoy escribiendo un código en este momento que genera objetos que se parecen un poco a esto

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

    public object ValueA;
    public object ValueB;
}

Cosas bastante complicadas, creo que estarás de acuerdo. La cosa es queValueA solo puede ser de unos pocos tipos determinados (digamosstring, int yFoo (que es una clase) yValueB puede ser otro pequeño conjunto de tipos. No me gusta tratar estos valores como objetos (quiero la sensación cálida y cómoda de la codificación con un poco de seguridad de tipo).

Así que pensé en escribir una pequeña clase de contenedor trivial para expresar el hecho de que ValueA lógicamente es una referencia a un tipo en particular. Llamé a la claseUnion porque lo que estoy tratando de lograr me recordó el concepto de unión en 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);
    }
}

Usar esta clase ValueWrapper ahora se ve así

public class ValueWrapper2
{
    public DateTime ValueCreationDate;
    public  Union<int, string, Foo> ValueA;
    public  Union<double, Bar, Foo> ValueB;
}

que es algo parecido a lo que quería lograr, pero me falta un elemento bastante crucial: la verificación de tipos forzada por el compilador al llamar a las funciones Is y As, como lo demuestra el siguiente código

    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>();
        }
    }

OMI No es válido preguntarle a ValueA si es unchar Dado que su definición dice claramente que no lo es, este es un error de programación y me gustaría que el compilador se percate de esto. [Además, si pudiera hacerlo correctamente, entonces (con suerte) también obtendría inteligencia, lo que sería una bendición.]

Para lograr esto, me gustaría decirle al compilador que el tipoT puede ser uno de A, B o 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;
    } 

¿Alguien tiene alguna idea si lo que quiero lograr es posible? ¿O soy simplemente estúpido por escribir esta clase en primer lugar?

Gracias por adelantado.

Respuestas a la pregunta(15)

Su respuesta a la pregunta