我知道您可以使用FlagsAttribute
指示编译器使用位域进行枚举。
有没有办法指定枚举值不能与按位OR组合?
示例:
enum OnlyOneOption
{
Option1,
Option2,
...
}
在这个例子中,没有什么能阻止开发人员编写OnlyOneOption.Option1 | OnlyOneOption.Option2
。如果可能的话,我想在编译时禁止它。
答案 0 :(得分:27)
最近,Eric Lippert(他在微软期间从事C#编译器工作的人之一)blogged关于他对C#的十大遗憾,第四个就是那个
在C#中,枚举只是基础整数类型的薄类型系统包装。枚举上的所有操作都被指定为实际上对整数的操作,枚举值的名称类似于命名常量。
原则上,你不能让编译器阻塞
OnlyOneOption option = OnlyOneOption.Option1 | OnlyOneOption.Option2;
因为就整数而言,这种操作看起来非常好。正如您所指出的,您可以做的是不提供FlagsAttribute
- 这对开发人员来说已经很好了。
自enum
上的you cannot overload operators以来,您必须求助于运行时检查。
您可以做的是,只要您需要枚举,检查确切的相等性,并在使用值组合时throw
异常。最快捷,最干净的方法是使用switch
:
// Use the bit pattern to guarantee that e.g. OptionX | OptionY
// never accidentally ends up as another valid option.
enum OnlyOneOption { Option1 = 0x001, Option2 = 0x002, Option3 = 0x004, ... };
switch(option) {
case OnlyOneOption.Option1:
// Only Option1 selected - handle it.
break;
case OnlyOneOption.Option2:
// Only Option2 selected - handle it.
break;
default:
throw new InvalidOperationException("You cannot combine OnlyOneOption values.");
}
如果您不坚持使用enum
,则可以使用经典(Java-ish)静态模式:
class OnlyOneOption
{
// Constructor is private so users cannot create their own instances.
private OnlyOneOption() {}
public static OnlyOneOption OptionA = new OnlyOneOption();
public static OnlyOneOption OptionB = new OnlyOneOption();
public static OnlyOneOption OptionC = new OnlyOneOption();
}
此处OnlyOneOption option = OnlyOneOption.OptionA | OnlyOneOption.OptionB;
将失败,错误CS0019:" 运算符' |'不能应用于'OnlyOneOption'类型的操作数和' OnlyOneOption' "。
缺点是您失去了编写switch
语句的能力,因为它们实际上需要将编译时常量转换为int
(我尝试提供返回static public implicit operator int
的{{1}} 1}}字段,但即使这还不够 - "预期值为常数")。
答案 1 :(得分:5)
不,你无法阻止这一点。不指定[Flags]
属性不会禁止以下用户:
enum Option
{
One = 1,
Two,
Three
}
var myOption = Option.One | Option.Two;
此外,完全合法的内容如下:
var myOption = 0; //language quirk IMO, 0 being implicitly convertible to any enumeration type.
或
var myOption = (Option)33;
这是一个问题吗?不,不是真的;如果您的逻辑只考虑选项One
,Two
或Three
,那么只需执行它:
public Foo Bar(Option myOption)
{
switch (myOption)
{
case Option.One: ...
case Option.Two: ...
case Option.Three: ...
default: //Not a valid option, act in consequence
}
}
请注意,如果您有(可疑)代码根据枚举的基础值决定:
if (myOption < Option.Three) { //expecting myOption to be either One or Two...
然后你肯定没有使用正确的工具来完成工作; myOption = 0;
或myOption = (Option)-999
会有问题。
答案 2 :(得分:2)
添加验证
enum MyEnum
{
Value1 = 1,
Value2 = 2
}
MyEnum e = MyEnum.Value1 | MyEnum.Value2;
var isValid = Enum.GetValues(typeof(MyEnum)).Cast<MyEnum>().Count(x => e.HasFlag(x)) == 1;