Question

I have a case where I have several sets of numbers (register values). I want to improve readability and also to check appropriate types (only certain values make sense in certain functions).

In my particular implementation, I made them enums - so I have a set of enums now.

Now I seem to have reached the end on this approach, since I want to divide them into sets of valid enums for certain applications - so function A could for example take (a value from) enumA, enumB and enumC as input, but not enumD which is a description of different functionality.

I already looked into enums in interfaces and enum inheritance - both are dead ends, not possible in C#.

I wonder now how the solution to this problem might look like. I would like to get intellisense on the possible values and also have some type safety, so that I could not (well, at least not without maliciously casting it) feed the wrong values in.

How to achieve this?

(Possible solutions would be to simply write several functions taking several different enums - still possible but not really nice, or something like Is there a name for this pattern? (C# compile-time type-safety with "params" args of different types) - both just seems not too nice.)

Was it helpful?

Solution

One option is to scrap enums and use your own clases designed to mimic enums. It will be a bit more work for you to set them up, but once you do it will be easy enough to use, and will be able to have the functionality you've described.

public class Register
{
    private int value;

    internal Register(int value)
    {
        this.value = value;
    }

    public static readonly Register NonSpecialRegister = new Register(0);
    public static readonly Register OtherNonSpecialRegister = new Register(1);

    public static readonly SpecialRegister SpecialRegister 
        = SpecialRegister.SpecialRegister;
    public static readonly SpecialRegister OtherSpecialRegister 
        = SpecialRegister.OtherSpecialRegister;

    public override int GetHashCode()
    {
        return value.GetHashCode();
    }
    public override bool Equals(object obj)
    {
        Register other = obj as Register;
        if (obj == null)
            return false;

        return other.value == value;
    }
}

public class SpecialRegister : Register
{
    internal SpecialRegister(int value) : base(value) { }

    public static readonly SpecialRegister SpecialRegister = new SpecialRegister(2);
    public static readonly SpecialRegister OtherSpecialRegister = new SpecialRegister(3);
}

Given this, you could have a method like:

public static void Foo(Register reg)
{
}

That could take any register, and could be called like:

Foo(Register.NonSpecialRegister);
Foo(Register.OtherSpecialRegister);

Then you could have another method such as:

public static void Bar(SpecialRegister reg)
{
}

Which wouldn't be able to accept a Register.NonSpecialRegister, but could accept a Register.OtherSpecialRegister or SpecialRegister.SpecialRegister.

OTHER TIPS

Sounds like you have exhausted the capabilities of the static type system on the CLR. You can still get runtime validation by wrapping each integer with a class that validates that the value you try to store in it actually is a member of the static set.

If you have a reliable test suite or are willing to do manual testing this will at least catch the bugs instead of the bugs causing silent data corruption.

If you have multiple "sets" that you want to keep apart you can either use class inheritance or have a set of user-defined conversion operators which validate that the conversion is OK at runtime.

I don't know what specific requirements you have but maybe you can use class-based inheritance to check some properties statically. The base class would be the larger set in that case and derived classes would specialize the set of allowed values.

You have basically two options:

Option 1: Multiple enums

Create multiple enums, one for each application, and replicate the values in each enum. Then you can cast between them. For example:

enum App1
{
    Data1 = AppAll.Data1,
    Data2 = AppAll.Data2,
    Data42 = AppAll.Data42,
}

enum App2
{
    Data2 = AppAll.Data2,
    Data16 = AppAll.Data16,
    Data42 = AppAll.Data42,
}

enum AppAll
{
    Data1 = 1,
    Data2 = 2,
    Data16 = 16,
    Data42 = 42,
}

App1 value1 = (App1)AppAll.Data2;
App2 value2 = (App2)value1;

This will give you IntelliSense.

Option 2: Determine which are allowed

Create a method that returns a boolean on which values are allowed (this may be virtual and overridden for each application). Then you can throw an exception when the enum value is wrong.

public bool IsAllowed(AppAll value)
{
    return value == AppAll.Data2
        || value == AppAll.Data16
        || value == AppAll.Data42;
}


if (!IsAllowed(value))
    throw new ArgumentException("Enum value not allowed.");

This will not give you IntelliSense.


A few notes:

  • You cannot have inheritance for enums because under the covers enums are represented as structs (i.e. value types).
  • In C# you can literally cast any value to your enum type, even when it is not a member of it. For example, I can do (App1)1337 even when there is no member with value 1337.

If you want compile type checking, you are better off with distinct enums for distinct cases. If you want to have a master enum with all of your possibilities you can write a test that ensures that all of your "child" enum lists are valid subsets of the master (in terms of Int casts).

As an alternative, I would have to wonder (since no code is provided, I can only wonder) if you might not be better served with objects with methods for each enum option. Then you inherit out objects with the various methods instead of enums. (After all, it seems that you are using Enums as proxies for method signatures).

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top