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

  • Home
  • SEARCH
  • 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: Let me appologize for the length of this question, i had to put
(NOTE: This question is not about escaping queries, it's about escaping results) I'm using
Moderator note: This question is not a good fit for our question and answer
Note: Originally this question was asked for PostgreSQL, however, the answer applies to almost
Reading this question I found this as (note the quotation marks) code to solve
Note This is not a REBOL-specific question. You can answer it in any language.
Note : The code in this question is part of deSleeper if you want
( This question about refactoring F# code got me one down vote, but also
Note: This is the opposite direction to most similar questions! I have an iPhone
Note: This was posted when I was starting out C#. With 2014 knowledge, I

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.