C#: enums and static classes cannot be inherited, so what can I use instead?

366 Views Asked by At

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.

2

There are 2 best solutions below

2
On

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/

0
On

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.