0

I recently started using enums for commonly used names in my application. The issue is that enums cannot be inherited. This is the intent:

public enum foreignCars
   {
     Mazda = 0,
     Nissan = 1,
     Peugot = 2
   }


public enum allCars : foreignCars
   {
     BMW = 3,
     VW = 4,
     Audi = 5
   }

Of course, this can't be done. In similar questions I found, people have suggested using classes for this instead, like:

        public static class foreignCars
    {
        int Mazda = 0;
        int Nissan = 1;
        int Peugot = 2;
    }


    public static class allCars : foreignCars
    {
        int BMW = 3;
        int VW = 4;
        int Audi = 5;
    }

But static classes can't be inherited either! So I would have to make them non-static and create objects just to be able to reference these names in my application, which defeats the whole purpose.

With that in mind, what is the best way to achieve what I want - having inheritable enum-like entities (so that I don't have to repeat car brands in both enums/classes/whatever), and without having to instantiate objects either? What is the proper approach here? This is the part that I couldn't find in any other questions, but if I missed something, please let me know.

EDIT: I would like to point out that I have reviewed similar questions asking about enum and static class inheritance, and I am fully aware that is not possible in C#. Therefore, I am looking for an alternative, which was not covered in these similar questions.

  • 1
    Duplicate of [Enum "Inheritance"](https://stackoverflow.com/questions/757684/enum-inheritance), [Why can't I inherit static classes?](https://stackoverflow.com/q/774181/8967612) – 41686d6564 stands w. Palestine Dec 08 '20 at 15:41
  • 7
    You're almost certainly doing the wrong thing here. You effectively have a database of things, not an enumeration. – DavidG Dec 08 '20 at 15:43
  • 1
    There might be some possibility with a class with a protected constructor and static properties, which are the possible instances (see https://dotnetfiddle.net/Us13gZ ). But I agree with @DavidG Don't do this. – SomeBody Dec 08 '20 at 15:45
  • DavidG is right, but if you want to solve your problem this way, then take a look at records, I think they can be suitable in this case. – Ivan Khorin Dec 08 '20 at 15:46
  • Maybe you could use constants in static classes but instead of real inheritance use nesting. So you can call them like `AllCars.ForeignCars.Mazda` or `AllCars.VW` – Tim Schmelter Dec 08 '20 at 15:46
  • non-static classes can have static members which can be accessed without creating instances of those classes. But still this doesn't look quite right – Damien_The_Unbeliever Dec 08 '20 at 16:02
  • You can't. To be able to mimic an inheritance from enums as well as static classes, you can use a generic inheritable singleton pattern. For enums you replace them by a list of values (or consts or readonly members) and you can implement the IEnumerable behavior as well as min, max, next, previous... For a such singleton you can take a look at this article for ideas (I have not published my current code that is more advanced but dev is delayed) : https://www.ordisoftware.com/blog/2009/07/design-flaws-of-the-singleton-pattern-in-csharp/ –  Dec 08 '20 at 16:05
  • Okay, guys, I get that my approach is bad here, so what would be the right approach? I want to have these values (car names) as generic human-readable constants that I could use everywhere in my project. Creating objects doesn't make sense here, as they would carry no data or methods inside. I just need a name. Basically enums are perfect for this, but they don't allow inheritance, which I really need here too. What would be a better way then? – Justinas Rubinovas Dec 08 '20 at 17:10
  • @41686d6564, this is not a duplicate, I already read these questions, and I know these things asked in these questions can't be done - I am asking for an alternative, correct approach. – Justinas Rubinovas Dec 08 '20 at 17:11
  • The solution depends on how you use this. If it's to map string values to unique integers, perhaps a singleton `Dictionary`. It's not a compile-time check, but it *does* map. Alternatively, update the original in your code. – Joel Coehoorn Dec 08 '20 at 19:08
  • What's wrong with just one enum class that you keep adding to? Do you want to make this a sealed up library but want others to be able to add more values later? – Dan Csharpster Dec 08 '20 at 19:28
  • @DanCsharpster I want to iterate through specific enums, so it is better to have them separated, yet having these common values when I need them. – Justinas Rubinovas Dec 08 '20 at 19:30
  • I would agree this is not _necessarily_ a duplicate of the one proposed by others, but I still find the question lacking. It is way too open-ended to be appropriate for Stack Overflow, and has the smell of [XY Problem](https://meta.stackexchange.com/questions/66377/what-is-the-xy-problem) all over it. This question might be more appropriate on softwareengineering.stackexchange.com, _if_ you can fix it so that instead of asking about a specific implementation choice that just won't work anyway, you provide a better description of the requirement that led you to this bad choice in the 1st place. – Peter Duniho Dec 08 '20 at 19:37
  • You are starting to tread into database territory. Enums are meant for simple lookups. If you don't want a full blown database, you could create a json config file in any structure you wanted, maybe an id with a type, then load it up on application start. I think you are trying to kind of fit a square peg into a round hole here. Enums weren't really meant for this. You could also store all these enums as instance values under a parent config class and use reflection to iterate over them, but that would also be an example of using language features for something other than intended. – Dan Csharpster Dec 08 '20 at 19:38

2 Answers2

1

Enums in C# are a pretty weak implementation. They're basically just named values. Java's enums are a little nicer as they're actually classes with special semantics, so you could implement something similar to Java's enums...

public sealed class Car
{
    public static readonly Mazda = new Car(0, "Mazda", true);
    public static readonly Nissan = new Car(1, "Nissan", true);
    public static readonly Peugeot = new Car(2, "Peugeot", true);
    public static readonly BMW = new Car(3, "BMW", false);
    public static readonly VW = new Car(4, "VW", false);
    public static readonly Audi = new Car(5, "Audi", false);

    private Car(int value, string name, bool isForeign)
    {
        Value = value;
        Name = name;
        IsForeign = isForeign;
    }

    public int Value { get; }
    public string Name { get; }
    public bool IsForeign { get; }
    
    // TODO : Implement Equals and GetHashCode...
    public override string ToString() => Name;
}

Notice that the only instances of Car that can be created here are declared within the class itself, and no instances can be constructed externally because the constructor is private. This behaves much like an enum in Java, and allows you to include additional fields to the enum, rather than just name and value.

One more thing to note here... IsForeign is declared as a bool. Foreign to you, might not be foreign to me, so consider globalization and localization too. Maybe you should specify Car by CountryOfManufacture or better still, something from .NET's Culture namespace.

If you're wondering where I got the inspiration for enum classes...Jimmy Bogard, the author of Automapper.

https://lostechies.com/jimmybogard/2008/08/12/enumeration-classes/

Matthew Layton
  • 39,871
  • 52
  • 185
  • 313
  • As much as this _might_ solve your problem, I'd seriously consider your implementation and perhaps move this data into a database or something (mutable) rather than an enum (immutable). – Matthew Layton Dec 08 '20 at 19:26
  • Another comment on `CountryOfManufacture` - Maybe the wrong name since some cars are manufactured in many countries. `CountryOfOrigin` referring to their head office might be more relevant...but again, you can't rely on this being an immutable fact. – Matthew Layton Dec 08 '20 at 19:29
1

The general problem you're having is that you're attaching additional meaning to an enum. Once you do that it's no longer an enum it's a concept within your system which is usually something represented by an object.

Without knowing more about what you're doing it's hard to offer much more than work arounds. But the point is that "extra" information you want to attach, doesn't belong in the enum. It needs to be located somewhere else. I'll offer a simple example that leaves a lot to be desired but at least represents the concept outside the enum:

public enum allCars : int
{
    // note in general this is a bad idea, you've
    // created compile time construct to hold 
    // something that can change.
    Mazda = 0,
    Nissan = 1,
    Peugot = 2,
    BMW = 3,
    VW = 4,
    Audi = 5
}

public class Car
{
    static HashSet<allCars> foreignCars = new HashSet<allCars>(
            new allCars[] { allCars.Mazda, allCars.Nissan, allCars.Peugot });

    public Car(allCars id)
    {
        this.CarId = id;
    }

    public allCars CarId { get; }
    public bool IsForeignCar => foreignCars.Contains(this.CarId);
}

I've moved that idea of "is it a foreignCar" into the Car class itself which is probably dicey but it illustrates the general point. Having things set at compile time that can change - the enum itself and the pool of foreign cars - is usually not great. You'll probably want to refactor that out of the design as well.

MikeJ
  • 1,299
  • 7
  • 10