tags:

views:

104

answers:

5

Hi, I have an enum and i want to "hide" one of its values (as i add it for future support). The code is written in C#.

public enum MyEnum
{
   ValueA = 0,

   ValueB = 1,

   Reserved
}

I don't want to allow peoples who use this code to use this values (MyEnum.Reserved). Any idea? TIA

+2  A: 

This is not possible in C#. All enum values are accessible if the Enum itself is accessible.

The only way you could simulate accomplishing this is by using a less accessible static field that used an integer value not already used in the Enum.

public enum MyEnum {
  ValueA = 0;
  ValueB = 1;
}

internal static class MyEnumEx {
  internal static MyEnum Reserved = (MyEnum)42;
}

I'm curious though as to why you would want to do this. No matter what you do users can still provide the Reserved value. All that needs to be done is to cast an int of the appropriate value to the MyEnum type.

// Code that shouldn't access Reserve
MyEnum reserved = (MyEnum)42;  // Woot!
JaredPar
Nice comprehensive answer.
Kirk Woll
A: 

You can't hide it, but you can add a comment that this value doesn't have any effect at this moment. Otherwise just remove it and add it if you add the support, this shouldn't break any other code dependent on it, as long as the original values don't change.

Femaref
+2  A: 

You can achieve something like this by using your own custom type instead of enum:

// This type works pretty much the same way an enum works;
// each specific value can be cast to/from an int, and each has
// a specific name that is returned on calling ToString().
public sealed class MyEnum
{
    private readonly int _value;
    private readonly string _name;

    // private constructor -- ensure that the static members you define below
    // are the only MyEnum instances accessible from any outside code
    private MyEnum(int value, string name)
    {
        _value = value;
        _name = name;
    }

    // no need to override Equals or GetHashCode, believe it or not --
    // one instance per value means we can use reference equality and
    // that should be just fine
    public override string ToString()
    {
        return _name;
    }

    // provide direct access only to these members
    public static readonly MyEnum ValueA = new MyEnum(0, "ValueA");
    public static readonly MyEnum ValueB = new MyEnum(1, "ValueB");

    // this member is only available to you within the current assembly
    internal static readonly MyEnum Reserved = new MyEnum(-1, "Reserved");
}

You could even further emulate the behavior of enum values by, for example, overloading the explicit operators to convert to/from MyEnum objects to int values (took JaredPar's suggestion to use this rather than implicit):

public static explicit operator MyEnum(int value)
{
    switch (value)
    {
        case 0:
            return ValueA;
        case 1:
            return ValueB;
        default:
            throw new InvalidCastException();
    }
}

public static explicit operator int(MyEnum value)
{
    return value._value;
}
Dan Tao
Then "bad" developers could simple do this. `MyEnum reserved = -1;` and the OP is back where he started. Additionally you'd need to override equality operations in order to get value semantics back
JaredPar
@JaredPar: But overloading the `implicit` operator and only accepting 0 or 1 would specifically preclude this, would it not? I also considered overriding equality operations but actually I don't think it's necessary since there is only one instance of a `MyEnum` object per value (due to the private constructor and only specific static readonly members), which means that reference equality effectively *becomes* value equality for this type.
Dan Tao
@Dan, ah I see how you've subverted that trick. Nice. It would be better if this was explicit vs. implicit though or it'd lead to subtle runtime bugs.
JaredPar
+5  A: 

If you don't want to show it, then don't include it:

public enum MyEnum
{
    ValueA = 0,
    ValueB = 1,
}

Note that a user of this enum can still assign any integer value to a variable declared as MyEnum:

MyEnum e = (MyEnum)2;  // works!

This means that a method that accepts an enum should always validate this input before using it:

void DoIt(MyEnum e)
{
    if (e != MyEnum.ValueA && e != MyEnum.ValueB)
    {
        throw new ArgumentException();
    }

    // ...
}

So, just add your value later, when you need it, and modify your methods to accept it then.

dtb
In your last example you could use `Enum.IsDefined` (http://msdn.microsoft.com/en-us/library/system.enum.isdefined.aspx). That usually would be much simpler than checking every value, and it is also future-proof (you would not hava to modify the method if a new value is added to the enum).
0xA3
@0xA3: The Framework Design Guidelines recommend against this: **"DO NOT use Enum.IsDefined for enum range checks."** One reason is performance (Enum.IsDefined internally uses reflection and is very slow); the other reason is exactly the versioning problem where the use of Enum.IsDefined makes the code error prone when the enum is extended. It's better to avoid Enum.IsDefined and do the checks manually.
dtb
+2  A: 

You could use the 'Obsolete' attribute - semantically incorrect, but it will do what you want:

public enum MyEnum { 
  ValueA = 0, 
  ValueB = 1, 
  [Obsolete("Do not use this", true)]Reserved 

}

Anyone who tries to compile using the Reserved item will get an error

Ray