Sign Up

Sign Up to our social questions and Answers Engine to ask questions, answer people’s questions, and connect with other people.

Have an account? Sign In

Have an account? Sign In Now

Sign In

Login to our social questions & Answers Engine to ask questions answer people’s questions & connect with other people.

Sign Up Here

Forgot Password?

Don't have account, Sign Up Here

Forgot Password

Lost your password? Please enter your email address. You will receive a link and will create a new password via email.

Have an account? Sign In Now

You must login to ask a question.

Forgot Password?

Need An Account, Sign Up Here

Please briefly explain why you feel this question should be reported.

Please briefly explain why you feel this answer should be reported.

Please briefly explain why you feel this user should be reported.

Sign InSign Up

The Archive Base

The Archive Base Logo The Archive Base Logo

The Archive Base Navigation

  • SEARCH
  • Home
  • About Us
  • Blog
  • Contact Us
Search
Ask A Question

Mobile menu

Close
Ask a Question
  • Home
  • Add group
  • Groups page
  • Feed
  • User Profile
  • Communities
  • Questions
    • New Questions
    • Trending Questions
    • Must read Questions
    • Hot Questions
  • Polls
  • Tags
  • Badges
  • Buy Points
  • Users
  • Help
  • Buy Theme
  • SEARCH
Home/ Questions/Q 916927
In Process

The Archive Base Latest Questions

Editorial Team
  • 0
Editorial Team
Asked: May 15, 20262026-05-15T18:07:38+00:00 2026-05-15T18:07:38+00:00

[Note: This question had the original title " C (ish) style union in C#

  • 0

[Note: This question had the original title "C (ish) style union in C#"
but as Jeff’s comment informed me, apparently this structure is called a ‘discriminated union’]

Excuse the verbosity of this question.

There are a couple of similar sounding questions to mine already in SO but they seem to concentrate on the memory saving benefits of the union or using it for interop.
Here is an example of such a question.

My desire to have a union type thing is somewhat different.

I am writing some code at the moment which generates objects that look a bit like this

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

    public object ValueA;
    public object ValueB;
}

Pretty complicated stuff I think you will agree. The thing is that ValueA can only be of a few certain types (let’s say string, int and Foo (which is a class) and ValueB can be another small set of types. I don’t like treating these values as objects (I want the warm snugly feeling of coding with a bit of type safety).

So I thought about writing a trivial little wrapper class to express the fact that ValueA logically is a reference to a particular type. I called the class Union because what I am trying to achieve reminded me of the union concept in 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);
    }
}

Using this class ValueWrapper now looks like this

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

which is something like what I wanted to achieve but I am missing one fairly crucial element – that is compiler enforced type checking when calling the Is and As functions as the following code demonstrates

    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 It is not valid to ask ValueA if it is a char since its definition clearly says it is not – this is a programming error and I would like the compiler to pick up on this. [Also if I could get this correct then (hopefully) I would get intellisense too – which would be a boon.]

In order to achieve this I would want to tell the compiler that the type T can be one of A, B or 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;
    } 

Does anyone have any idea if what I want to achieve is possible? Or am I just plain stupid for writing this class in the first place?

  • 1 1 Answer
  • 0 Views
  • 0 Followers
  • 0
Share
  • Facebook
  • Report

Leave an answer
Cancel reply

You must login to add an answer.

Forgot Password?

Need An Account, Sign Up Here

1 Answer

  • Voted
  • Oldest
  • Recent
  • Random
  1. Editorial Team
    Editorial Team
    2026-05-15T18:07:39+00:00Added an answer on May 15, 2026 at 6:07 pm

    I don’t really like the type-checking and type-casting solutions provided above, so here’s 100% type-safe union which will throw compilation errors if you attempt to use the wrong datatype:

    using System;
    
    namespace Juliet
    {
        class Program
        {
            static void Main(string[] args)
            {
                Union3<int, char, string>[] unions = new Union3<int,char,string>[]
                    {
                        new Union3<int, char, string>.Case1(5),
                        new Union3<int, char, string>.Case2('x'),
                        new Union3<int, char, string>.Case3("Juliet")
                    };
    
                foreach (Union3<int, char, string> union in unions)
                {
                    string value = union.Match(
                        num => num.ToString(),
                        character => new string(new char[] { character }),
                        word => word);
                    Console.WriteLine("Matched union with value '{0}'", value);
                }
    
                Console.ReadLine();
            }
        }
    
        public abstract class Union3<A, B, C>
        {
            public abstract T Match<T>(Func<A, T> f, Func<B, T> g, Func<C, T> h);
            // private ctor ensures no external classes can inherit
            private Union3() { } 
    
            public sealed class Case1 : Union3<A, B, C>
            {
                public readonly A Item;
                public Case1(A item) : base() { this.Item = item; }
                public override T Match<T>(Func<A, T> f, Func<B, T> g, Func<C, T> h)
                {
                    return f(Item);
                }
            }
    
            public sealed class Case2 : Union3<A, B, C>
            {
                public readonly B Item;
                public Case2(B item) { this.Item = item; }
                public override T Match<T>(Func<A, T> f, Func<B, T> g, Func<C, T> h)
                {
                    return g(Item);
                }
            }
    
            public sealed class Case3 : Union3<A, B, C>
            {
                public readonly C Item;
                public Case3(C item) { this.Item = item; }
                public override T Match<T>(Func<A, T> f, Func<B, T> g, Func<C, T> h)
                {
                    return h(Item);
                }
            }
        }
    }
    
    • 0
    • Reply
    • Share
      Share
      • Share on Facebook
      • Share on Twitter
      • Share on LinkedIn
      • Share on WhatsApp
      • Report

Sidebar

Related Questions

Note: this question is related to this one , but two years is a
Note: This is similar to this question but it is not the same. I
Note: Let me appologize for the length of this question, i had to put
Note: This might seem like a Super User question at first, but please read
Note this question was originally posted in 2009, before C++11 was ratified and before
Note: This question has broadened in scope from previous revisions. I have tried to
Note: This question is related to an advice rather than an issue. I would
Please note this question related to performance only. Lets skip design guidelines, philosophy, compatibility,
Note: In this question I'm using the term autocomplete (or iterative search) to refer
(note that this question is not about CAS, it's about the May fail spuriously

Explore

  • Home
  • Add group
  • Groups page
  • Communities
  • Questions
    • New Questions
    • Trending Questions
    • Must read Questions
    • Hot Questions
  • Polls
  • Tags
  • Badges
  • Users
  • Help
  • SEARCH

Footer

© 2021 The Archive Base. All Rights Reserved
With Love by The Archive Base

Insert/edit link

Enter the destination URL

Or link to existing content

    No search term specified. Showing recent items. Search or use up and down arrow keys to select an item.