在方法参数中约束枚举值

时间:2010-04-21 19:08:08

标签: c# .net parameters enums constraints

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


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

抱歉这个蹩脚的例子,但希望你能得到这个想法。有没有办法约束PeelFruit将接受的枚举值?

很明显我可以使用开关或其他东西在方法中检查它们,但是如果有办法这样做会很酷a)更紧凑一些,并且b)会导致编译时错误,而不是运行时错误。

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

8 个答案:

答案 0 :(得分:7)

这对于基本语言功能是不可能的(虽然code contracts可以使用compile-time checking is only available with the premium edition。实际上,您甚至无法将输入限制在enum中定义的值!接受Fruit参数的方法将接受任何int(或枚举的类型,如果它不是整数),只要调用者首先将其转换为Fruit

PeelFruit((Fruit)10000); // Not a Fruit? Not a problem!

答案 1 :(得分:3)

从你的例子中不清楚你真正想要做什么。你不能像你建议的那样对枚举有约束,因为枚举的所有值都是等价的并且是相同的类型。编译器只关心类型,而不关心值,因此它无法区分。

如果由于某些特殊原因不需要枚举,那么你可以实现相同的一种方法就是使用类型系统。而不是枚举,定义一个对象层次。如果你愿意,物品本身可以是单独的占位符。这样您就可以使用类型系统来执行此操作。作为一个例子

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 ....) { }

有意义吗?

答案 2 :(得分:1)

您可以使用code contracts进行编译器检查。我相信你可以这样做:

Contract.Requires(fruit != Fruit.Strawberry) 

答案 3 :(得分:1)

可能的方法是使用对象来代表水果,而不是使用枚举。这将使您能够进行编译时检查。

每个水果类都会实现接口来说明他们允许或不允许的内容。

而不是

void PeelFruit(Fruit fruit) { ... }

它将是

void PeelFruit(IPeelable fruit) { ... }

答案 4 :(得分:0)

一个非常简单的方法是在示例中分配数值

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

然后检查

if ((int) thefruit > 2)
...
else
...

答案 5 :(得分:0)

有2个枚举,一个用于可剥离的水果,一个用于不可剥离的水果?如果枚举不适用于所有情况,那么无论如何它们应该是分开的东西。

这可能更好地解决了使用带有fruit,peelableFruit等实例的类型层次结构,它会强制编译器进行类型检查,这无论如何都不会真正在Enum上完成,正如已经指出的那样。

您可以使用没有功能的Marker接口来识别类型并允许它们传递给方法。

答案 6 :(得分:0)

你提出的是一个属性,它是一个运行时实体,而不是编译时间。没有办法做你提出的建议,但你真的要问自己设计是否正确。如果你控制了枚举,可能有两个单独的枚举会更有意义。与PeelableFruitNonPeelableFruit一样。

答案 7 :(得分:0)

如果你想要编译时检查,那么我担心你不得不去上课。

如果你满足于运行时检查,那么最紧凑的形式将是一个检查值的函数。

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

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

如果您希望调用者能够验证他们没有传递错误的值,请将其设为公共函数。然后他们将不必处理异常。