views:

139

answers:

8
enum Fruit
{
    Banana,
    Orange,
    Strawberry
    ...
    ...
    // etc, very long enum
}


PeelFruit(Fruit.Orange);
PeelFruit(Fruit.Banana);
PeelFruit(Fruit.Strawberry); // huh? can't peel strawberries!

Sorry for the lame example, but hopefully you get the idea. Is there a way to constrain the enum values that PeelFruit will accept?

Obvisouly I could check them in the method with a switch or something, but it would be cool if there was a way to do it that is a) a bit more compact, and b) would cause a compile time error, not a run time error.

[Fruit = Orange,Bannana]
void PeelFruit(Fruit fruit) { ... }
A: 

Well one very easy way would be to assign numeric values so in your example

enum Fruit
{
    Banana = 1,
    Orange = 2,
    Strawberry = 4
    ...
    ...
    // etc, very long enum
}

Then check

if ((int) thefruit > 2)
...
else
...
Chris Marisic
A: 

Have 2 enums, one for peelable fruit, and one for non-peelable fruit? If the enum does not apply to all cases then they should probably be separate things anyway.

this is probably better solved using a type hierarchy with instances of fruit, peelableFruit etc, which would force the compiler to do type checking, which won't really be done on the Enum anyway, as has been pointed out.

You could use Marker interfaces, with no functionality, to identify the types and allow them to be passed to methods.

Sam Holder
+1  A: 

You can get the compiler check by using code contracts. I believe you can do something like this:

Contract.Requires(fruit != Fruit.Strawberry) 
Mark Byers
+3  A: 

This isn't possible with base language features (it's possible with code contracts, though compile-time checking is only available with the premium edition). In fact you can't even constrain your input to the values defined in your enum! A method that accepts a Fruit parameter will accept any int (or whatever the enumeration's type is, if it isn't an integer), as long as the caller casts it to Fruit first:

PeelFruit((Fruit)10000); // Not a Fruit? Not a problem!
Jeff Sternal
+1 Not only is this fact useful to point out, but "NAF?NAP!" made my day. :-)
Jeffrey L Whitledge
A: 

What you're proposing is an Attribute which is a run-time entity, not compile time. There's really no way to do what you're proposing, but you really have to ask yourself if the design is correct. If you're in control of the enum, maybe having two separate enums would make more sense. Like PeelableFruit and NonPeelableFruit.

BFree
+1  A: 

A possibility would be to use objects to represent the fruit instead of using enums. This would enable you to have compile time checks.

Each fruit class would implement interfaces to say what they allow or dont allow.

Instead of

void PeelFruit(Fruit fruit) { ... }

it would be

void PeelFruit(IPeelable fruit) { ... }
Richard R
+2  A: 

It isn't clear from your example what you're really trying to do. You can't have constraints on enums like you suggest because all values of an enum are equivalent and the same type. The compiler only cares about types, not values, so it can't differentiate.

One way you can achieve the same thing, if the enum is not required for some special reason, would be to use the type system to your advantage. Instead of the enum, define an object heirarchy. The objects themselves can be singleton place holders if you like. This way you can use the type system to do this. As an example

public abstract class Fruit { protected PeelableFruit() { } };
public abstract class PeelableFruit : Fruit { protected PeelableFruit() { } };
public class Orange : PeelableFruit { public static readonly Instance = new Orange(); protected Orange() { } };
etc. etc.

void AcceptsAnyFruit(Fruit ....) { }
void AcceptsPeelableFruit(PeelableFruit ....) { }

Make sense?

Stewart
Surely PeelableFruit should extend Fruit?
Samir Talwar
@Samir Talwar - quite right - typo
Stewart
A: 

If you want compile-time checks, then I'm afraid you'll have to go with a class heirarchy.

If you'll settle for run-time checks, then the most compact form would be a function that checks the value.

void PeelFruit(Fruit fruit)
{
    if (!Peelable(fruit))
        throw new ArgumentException("Fruit is nonpeelable."); 
    ... 
} 

static bool Peelable(Fruit fruit) { ... }

Make it a public function if you want your callers to be able to verify that they are not passing an incorrect value. Then they won't have to handle the exception.

Jeffrey L Whitledge