Constraining enum value in method parameter
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 limit the values ββthe enumeration will accept PeelFruit
?
Obvisouly I could check them in a method with a switch or something, but it would be great if there was a way to do this: a) a little more compact, and b) throw a compile time error, not a runtime error ...
[Fruit = Orange,Bannana]
void PeelFruit(Fruit fruit) { ... }
a source to share
This is not possible with basic language features (possibly with code contracts , although compile-time checking is only available in the list price ). In fact, you cannot even limit your input to the values ββspecified in enum
! A method that takes a parameter Fruit
will accept any int
(or any enumeration type if it is not an integer) as long as the caller passes it in first Fruit
:
PeelFruit((Fruit)10000); // Not a Fruit? Not a problem!
a source to share
It is not clear from your example what you are really trying to do. You cannot have restrictions on enums as you suggest, because all enumeration values ββare equivalent and the same. The compiler only cares about types, not values, so it cannot differentiate.
One way to achieve the same, if the enumeration is not required for some specific reason, would be to use the type system to your advantage. Instead of listing, specify the object hierarchy. The objects themselves can be single point holders if you like. So you can use the type system for that. 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?
a source to share
You can get compiler checking using code contracts . I believe you can do something like this:
Contract.Requires(fruit != Fruit.Strawberry)
a source to share
Perhaps it would be better to use objects to represent fruits instead of using enums. This will allow you to perform compile-time checks.
Each fruit class will implement interfaces to say what they allow or not allow.
Instead
void PeelFruit(Fruit fruit) { ... }
This will
void PeelFruit(IPeelable fruit) { ... }
a source to share
Are there 2 checklists, one for flaky fruits and one for uncompressed fruits? If the enumeration does not apply to all cases, then they probably should be different.
this is probably better handled by using a type hierarchy with examples of fruits, peelableFruit, etc., which forces the compiler to do type checking, which will not actually be done in an Enum as stated.
You can use Marker interfaces without any functionality to identify types and let them pass methods.
a source to share
What you are suggesting is an attribute, which is a runtime object, not compile time. There is really no way to do what you suggest, but you really need to ask yourself if the design is correct. If you are in control of an enumeration, it might make more sense to have two separate enumerations. As PeelableFruit
and NonPeelableFruit
.
a source to share
If you need compile time checks, I'm afraid you'll have to go with a class hierarchy.
If you agree to run-time validation, the most compact form would be a function that validates a 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 make sure they are not passing in the wrong value. Then they won't have to handle the exception.
a source to share