The Register Home Page

back to article Microsoft devs rejoice: Union types coming to C# in November

Microsoft is set to launch the next iteration of C# this November. Redmond recently unveiled a preview of C# 15 that introduces tighter restrictions on data types alongside enhanced memory safety protections. Perhaps the most frequently requested feature that arrives with this release is union types. Union types are not new to …

  1. Petalium

    Unions seems like a maintenance nightmare. Say you have a union of different types of managers and God makes a better idiot, now you have to extend the list and recompile instead of just add an entry in your database.

    Any real example when a union is better than a classification field in a record?

    1. Fido

      Any real example when a union is better than a classification field in a record?

      According to the article, "the union keyword can unite a collection of custom data types ('Dog,' 'Cat,' and 'Bird') as a single umbrella type, such as 'Pet.' "

      If classifying animals is not a real example, then almost all modern programming paradigms are in trouble.

      1. Petalium

        Yes I get that you can classify them as pets, what I’m asking is if there is any benefits to this approach that isn’t covered by a classification field in a record? And if you want to add your tiger as a pet do you have to recompile the program instead of just adding tiger with added information to a database( db in a broader sense as something where the data can be changed)

        1. Caspian Prince

          I think the only purpose to unions was to be able to allow disparate types to use the same memory space but be accessed in a sane looking way to a programmer. The animals example is terrible and is actually a really good way to describe why OOP paradigms are better than unions.

          A better example is this, where there might be no overlapping (ie. inheritable) characteristics between Animal, Mineral, or Vegetable...

          union Thing {

          Animal animal;

          Mineral mineral;

          Vegetable vegetable;

          }

          ..except that, again, this is better solved by simple OOP with an interface. So ... what exactly are unions going to bring to the table here apart from confusion, more complexity, and eventual scope for abuse and maintenance headaches.

    2. cyberdemon Silver badge

      In my experience, the union is only ever useful in C (not C++ and certainly not C#) as a way to create flexible data structures in a non object-oriented language. E.g. if you are serialising arbitrary data to a file or shared memory, or sending a message between two microcontrollers over CAN bus, you might declare a struct with a few bits of data type, and a union with the data which could be either float, int or a short string.

      That's about the only use for union datatypes that I have ever personally encountered.

      In a language like C#, maybe it can save memory but it feels like it would come with so many caveats that it's hardly worth it.

      1. Anonymous Coward
        Anonymous Coward

        Bypassing Strict Type-Checking

        I have (mis-) used undiscriminated variant records (aka unions) in Pascal-6000 to:

        (a) help me debug code which used pointers, and,

        (b) provide a quick-and-dirty way to print the members of sets.

        In case (a), I knew that a pointer was a 17-bit integer.

        In case (b), I knew a set was a 60-bit integer.

        Warning: this technique is machine- and compiler-dependent, and I urgently recommend against its use in general software. But it worked well-enough for student programming assignments. (I removed those things from the final, submitted program versions.)

      2. AMBxx Silver badge

        Is it for people who don't understand OO, inheritance and abstract classes?

        1. Bebu sa Ware Silver badge
          Coat

          Is it for people who don't understand OO, inheritance and abstract classes?

          Yes. ;)

      3. Richard 12 Silver badge
        Boffin

        This C# 'union' is what C++ calls Variant

        It's not a union in the C/C++ sense, because it knows the current type of object in the box. I guess they called it "union" to avoid a name collision with the "var" keyword.

        Success-or-error-object is usually done via exceptions, and having to recompile to add new types to the box makes it a lot more cumbersome than the widely used interface "IObject" base class technique.

        I guess this is actually intended for functions like deserializing that could return a specific two to three types. Eg a function that returns one of two IObject derived classes, or perhaps a single object vs container of objects where the single object case is common enough for IEnumerable overhead to matter.

        So yeah, rather specific edge cases.

    3. Filippo Silver badge

      That's not what unions are for, and the animals example is a poor one too. Both the managers and the animals can generally be better represented by database records or subclasses.

      The article mentions a typical use case for unions: the return value of a function that can return heterogeneous objects. For example, a success condition of a type, and a failure condition of a different type.

      You could do that with an interface or an abstract class, but if you do that, and then someone adds another implementation and passes it to your logic, it can get tricky. The nice bit of unions is that they cannot be further extended.

      If you are in a scenario where "cannot be extended" is not a nice bit, then you probably shouldn't use unions.

      1. Richard 12 Silver badge

        Why wouldn't you use an exception for failure, as the C# gods intended?

        1. Filippo Silver badge

          That's a bit simplistic. There are many scenarios where failures are not exceptional. The function might be interfacing with a physical device that has the ability to report failure states, in which case you would probably use an exception to handle the case where communication with the device itself fails.

          For example, I frequently interface with PLCs, and maybe I have a function that asks the PLC about the weight of something, but the PLC might need some time for the weight to settle before it's considered good. The PLC might have a bit for "the weight is stable" and a word for "the weight". If you return a union of Weight(int) and Unstable, attempts to use an unstable weight would fail at compile-time, which is nice. You would throw an exception if the connection to the PLC fails.

          1. AndrueC Silver badge
            Boffin

            If you return a union of Weight(int) and Unstable, attempts to use an unstable weight would fail at compile-time, which is nice. Wouldn't that be a runtime scenario though? Or are you talking about certain types of weights that are always unstable?

            1. Filippo Silver badge

              No. The function would return an object of type "union that's either Weight(int) or Unstable" and you have to resolve it in order to do something with it. You literally cannot even attempt to get the integer unless it's Weight(int); it would be a syntax error.

            2. Filippo Silver badge

              I realize I might not have been entirely clear... here's how it would work

              public record class Weight(int Value);

              public record class Invalid(string reason);

              public union WeighingResult(Weight, Invalid);

              WeighingResult res = FunctionThatTalksToPLC();

              switch (res)

              {

              case Weight w:

              doSomethingWith(w.Value);

              break;

              case Invalid i:

              goBotherTheUserWith(i.Reason);

              break;

              }

              I might be getting the pattern matching syntax wrong in the switch statement, but that's the idea. The check that you're not attempting to do something with an invalid weight is all compile-time. You can't get Value from WeighingResult, and you can't get Value from Invalid, it HAS to be from Weight, which is valid by definition.

              You can do the same with an abstract class - as long as you control all of the code it goes through. If there's a chance for anyone to subclass it without you knowing, you can run into problems.

              1. AndrueC Silver badge
                Boffin

                Ah, I get it. You're saying that you'd be forced to write code to handle an unstable weight reading so that it is correctly handled if/when it happens at runtime.

                Yes that's fair enough. I'd probably have had a WeightResult class which throws if code tries to access the weight value before it has checked a bool. Something like:

                int WeightResult.WeightValue()=unstableFlagHasBeenRead?WeightValue:throw ExUnstableFlagHasNotBeenCheckedYet;

                and

                bool WeightResult.WeightIsStable()

                {

                unstableFlagHasBeenRead=true;

                return weightIsStable;

                }

                Although that's a runtime check it's something that wouldn't get past first base so is almost as good. Almost :)

        2. iain666

          Because exceptions should be exceptional. Throwing an exception for failures in routine validation is madness.

          1. AndrueC Silver badge
            Boffin

            I think I'd phrase that differently. Exceptions should be used to immediately abandon a sequence of actions.. My design preference is RAII and raising an exception is a reasonable choice if you want to abandon something you're doing. It doesn't matter to me whether the cause of your abandonment is a serious inconsistency or just a value that is out of range or even just 'oh well, we don't need to do that'.

            Exceptions+RAII+appropriate handlers..

            The above three working together allow you to just 'get the hell out of Dodge' and know that whatever mess you've just created is going to be cleaned up.

      2. DanAU

        One of the points of a union, that seems to be missed by some comments, is that you can allow multiple different return types (or argument types) without those types having to share a common interface. It's the GraphQL or TypeScript concept of unions, not C unions.

        There's a concept of an "Either" type in some functional programming languages... That's essentially a union of two concrete types, like `string | Exception`

    4. Anonymous Coward
      Anonymous Coward

      If the new better kind of idiot is functionally different to the other kinds of idiot you used to support, merely adding an entry to your database would be insufficient. You'd need to manually find all the places where the better kind of idiot needs to be added to code to change behaviours. Bug central.

      Union types eliminate that kind of bug.

      --

      Concrete example:

      If you're suggesting the better idiot manager is just a type of manager:

      abstract class Manager {}

      class Idiot : Manager{}

      class BetterIdiot : Manager{}

      class Engineer{}

      Then don't union on each discrete manager sub-class, union on the base type, e.g.: union {Manager,Engineer} . Now you can add all kinds of idiot without recompiling!

      If you need to handle each idiot separately then you can do: union {Idiot,BetterIdiot,Engineer}. Now it's much harder to forget to add support everywhere. Bugs reduced.

      Please note there's no relation between managers and engineers in that hierarchy, as is correct.

    5. Elongated Muskrat Silver badge

      I think the point is that you are forced to add the code to handle your new type, or it won't compile. Better that than a run-time exception six months down the line because you forgot to implement an edge case.

      1. Anonymous Coward
        Anonymous Coward

        The point i was making is that the statement is either untrue or it's a positive.

        You can use unions and add new subclasses if "all" you want is a database entry (as per OP). This won't require recompilation as it doesn't affect the declared types in the union.

        For cases where you do change the declared types, obviously you'll be recompiling to *add* support for that new datatype in all the places thr union is used. Complaining that the compiler prevents you from forgetting to update one of the place is a bit silly.

        I believe OP didn't understand option 1 was possible, and then perhaps the sky began falling in when they realized the compiler would prevent mistakes by giving them compilation errors instead of letting them ship bugs as per the old "just add a discriminator property" approach from ninteen diggity six which they were comparing to.

  2. Ian Bush
    Stop

    The Sins of the Forefathers

    Just as an observation Fortran 2018 marked Equivalence, something very similar to a C union, as obsolescent and liable for deletion in a future standard

    1. abend0c4 Silver badge

      Re: The Sins of the Forefathers

      The difference being that these things were essentially a way of getting around the type system, which C# unions are not.

    2. Dan 55 Silver badge
      Headmaster

      Re: The Sins of the Forefathers

      Shouldn't that be C unions are something very similar to Fortran 57's Equivalence?

  3. AMBxx Silver badge

    Are we due a new language?

    Every so often, languages become so complex that a new language is launched that aims to remove all the stuff that's 'wrong' about an existing language.

    Over time, it becomes clear that there's stuff that could make life easier.

    It's added as the slight increase in complexity is deemed to be worth it.

    Eventually, we're back to stage 1 and it's time for a new language.

    1. John Miles

      Re: Are we due a new language?

      Must have a look and see if I still have the C# book I brought in back around 2002, where it explained all the reasons they didn't include things like default values for parameters (IIRC there were several I had grown use to in a different language) and see how many of them have been added since.

      1. AMBxx Silver badge
        Happy

        Re: Are we due a new language?

        'Other Language'

        Could that be the language that nobody want to admit using? Go on say 'Visual Basic'!

        Then look at PowerApps and realise that it's VB3 reborn without all the complexity of VB.Net.

        1. John Miles

          Re: Are we due a new language?

          No it was Delphi/Object Pascal - I've used VB but only to support legacy stuff where either VB coders weren't much use or had left. If I bother updating a CV I doubt I'll mention either, but I expect to retire after this job - just hoping they decide to pay me off.

  4. Caspian Prince
    Meh

    Not sure exactly what use this will be in the language...

    ...as unions were really all about nickel-and-diming limited memory in Old Fashioned C land. Perhaps slightly useful if they can be used to map directly to these unsafe C structures but there has never been a case in my career I've missed them when I've had the full power of plain ol' OOP at my disposal, which neatly solved the entire problem and rendered unions obsolete 40 years ago. Not that OOP is a panacea but it's very much looking like a better concept than unions ever were.

  5. Bebu sa Ware Silver badge
    Windows

    Reading the proposal…

    it looks more like a way of declaring a superclass (Pet) of the classes Cat, Dog and Bird with the restriction that the new superclass cannot be further subclassed (directly.) If you wanted to add Parrot the best you do might be subclass Bird.

    Would seem to have nothing in common with C's union which I think K & R described as a struct with all the members (fields) having the same offset.

    Presumably an instance of Pet is just a reference (~ pointer) to an object of Cat, Dog, Bird or (I wasn't clear about this) nil/null. I would imagine there is some run time identifer stored in Cat etc objects that might be inspected and allows the type of Pet to be explicitly determined.

    Strikes me not exactly a great leap forward from Simula67. ;)

    1. Dan 55 Silver badge

      Re: Reading the proposal…

      Not even C++ allows you to shoot yourself in the foot when defining unions. You're restricted to plain old C datatypes because there's nothing useful you can do with objects storing data in the same memory space.

      This seems to be like a hierarchy and could be done with a hierarchy, but it's not a hierarchy.

    2. AndrueC Silver badge
      Boffin

      Re: Reading the proposal…

      Presumably an instance of Pet is just a reference (~ pointer) to an object of Cat, Dog, Bird or (I wasn't clear about this) nil/null. I would imagine there is some run time identifer stored in Cat etc objects that might be inspected and allows the type of Pet to be explicitly determined.

      You can do that already in C# and have been able to since the inception of the language, decades ago.

      It's just run of the mill OOP stuff and using 'as' or 'is' or speculatively trying to cast an object to a subclass (the latter generally being a crappy way to do it). I have to say I'm struggling to understand what union brings to the already crowded C# table. I thought C# was getting overly complicated when I retired three years ago.

      Although being able to close a class seems to be touted as a new thing but I thought C# already had that ability. Ah yes, I was thinking of sealed which is not quite the same thing.

  6. Elongated Muskrat Silver badge

    Not mentioned here...

    ...is the new feature that is most likely to get used:

    The ability to break out of a loop at a particular level, by labelling the loop and using that loop on your break statement. Previsouly this sort of thing would ahve to be done by setting a flag in an inner loop before breaking, testing for it in the outer loop and breaking again, possibly through multiple levels. You end up with an awful lot of boilerplate; for example (apologies for lack of indentation, blame El Reg's formatting for this):

    var escaped = false;

    for (i = 0; i < 10; i++)

    {

    for (j = 0; j < 100; j++)

    {

    for (k = 0; k < 1000; k++)

    {

    if (i == 5 && j == 6 && k == 7)

    {

    escaped = true;

    break;

    }

    }

    if (escaped)

    {

    break;

    }

    }

    if (escaped)

    {

    break;

    }

    }

    becomes:

    outer: for (i = 0; i < 10; i++)

    {

    for (j = 0; j < 100; j++)

    {

    for (k = 0; k < 1000; k++)

    {

    if (i == 5 && j == 6 && k == 7)

    {

    break outer;

    }

    }

    }

    }

    On a side note, will El Reg ever get HTML code tags to work properly, retain formatting of code blocks and stop putting in loads of spurious line breaks?

    1. Dan 55 Silver badge
      Happy

      Re: Not mentioned here...

      Previsouly this sort of thing would ahve to be done by setting a flag in an inner loop before breaking, testing for it in the outer loop and breaking again, possibly through multiple levels.

      Well, I'd have used a goto.

      1. Elongated Muskrat Silver badge

        Re: Not mentioned here...

        Wash your mouth out with soap and water and go to your room right now!

        In seriousness, I understand where you're coming from, and this seems like a more controlled version of goto, so you don't have to use that. I think the last time I actually had to use a goto statement was some time in the 1980s, on a CPC464.

        If this leads to safer and more mainatianble code, then I'm all for it. Next up: methods that are only allowed a single return statement, right at the end...

        1. Dan 55 Silver badge

          Re: Not mentioned here...

          I'll accept that if you let me keep the goto to jump to it.

          It's pretty watered down in C/C++ anyway, it only works in the same function.

  7. Dizzy Dwarf

    It doesn't matter

    Nobody uses C-octothorpe anyway.

  8. trevorde Silver badge

    STOP!

    For the love of $DEITY, no more features. The language is already too complicated with features only a handful of devs actually use.

  9. picturethis
    Flame

    Just a way for MS to "encourage" people to get off of previous versions of Visual Studio

    This is just more MS "hand-waving" - they need to:

    1) Get people onto only the latest Visual Studio version (2026) - only this version supports .NET 10 and beyond (.NET 11)

    2) Obsolete using earlier Visual Studio versions as soon as a single developer on a team usings a single new feature of C#, everyone now needs to move to it.

    3) A way to support multiple inheritance in a perverted way, since C# has always had that limitation

    4) Show that C# doesn't need/want OOP. Apparently devs can't handle this concept called encapsulation and the decoupling that it can provide.

    It seems like all's that it takes is one C# developer at MS that has a bug up his/her ass because C# doesn't work the way they want it to, regardless of their complete lack of understanding of the most fundamental, time-proven software development practices to go off and propose a half-cocked/half-baked, moronic feature. The only one more moronic is the managers that allow the feature to advance up through the process.

    I wouldn't be surprised if this originated with an AI halucination / slop event generated by co-pilotless.

    BTW:

    VS2026 - has:

    - forced, automatic updates and when doing so, completely uninstalls any additional/older compiler tool chains, that one has to go back and re-install manually afterwards, if they are needed.

    incoming, 4,3,2,1 downvotes...

    1. Floor1

      Re: Just a way for MS to "encourage" people to get off of previous versions of Visual Studio

      Good excuse to use Rider instead.

POST COMMENT House rules

Not a member of The Register? Create a new account here.

  • Enter your comment

  • Add an icon

Anonymous cowards cannot choose their icon