我在C#中创建了一个标志枚举,类似于以下内容:
[Flags]
public enum DriversLicenseFlags
{
None = 0,
Suspended = 1 << 1,
Revoked = 1 << 2,
Restored = 1 << 3,
SuspendedAndRestored = Suspended | Restored,
RevokedAndRestored = Revoked | Restored,
}
关于我的意图的一些注释:
Suspended
和Revoked
是唯一可以但不一定会导致恢复的状态。Restored
只有在用户也是Suspended
或Revoked
(或两者)的情况下才有可能。重要的是要具体跟踪哪个事件是恢复的前兆。Suspended
和Revoked
(以及Restored
)另外,我正在努力坚持在MSDN Designing Flags Enumerations中提出的建议。特别是:
SuspendedAndRestored
和RevokedAndRestored
都会很常见。Restored
无效,除非至少设置了Suspended
和Revoked
中的一个。理想情况下,我希望Restored
的值存在于内部使用的枚举中,但只能通过某些有效组合公开设置。不幸的是,internal
不是枚举值的有效修饰符。
我想过几个替代品,但每个都有缺点:
将Restored
保留为公共值,请注意注释中的限制,并对公共API上的无效组合进行前置条件检查。
这样可行,很可能是我将采用的解决方案。但是,似乎他们应该是一个更清洁的解决方案。
使用here所述的增强的类似java的枚举,并将Restored
定义为internal static
。
这也可行,但感觉有点矫枉过正,因为此时我不需要任何其他功能。
不要将Restored
定义为值,而是保留OR的值,并检查使用方法中的值。即:
internal const int RestoredFlag = 1 << 3;
[Flags]
public enum DriversLicenseFlags
{
None = 0,
Suspended = 1 << 1,
Revoked = 1 << 2,
SuspendedAndRestored = Suspended | RestoredFlag,
RevokedAndRestored = Revoked | RestoredFlag,
}
无论是如何定义以及如何在内部使用它,这都让我感到很难过。
答案 0 :(得分:2)
为什么要特别使用Flags
?这不完全是你追求的吗?
public enum DriversLicenseStatus
{
None = 0,
Suspended,
Revoked,
SuspendedAndRestored,
RevokedAndRestored,
}
确保只在DriversLicenseStatus
属性的属性设置器中进行“有效”转换肯定会很容易。
如果由于某种原因你肯定想在内部使用Flags
,那么你可以定义一个单独的private enum DriversLicenseStatusFlags
并从中转换为仅在公共接口中公开DriversLicenseStatus
值。
另一个值得考虑的选择是将枚举分为两个值:
bool IsActive;
enum InactiveReason {
None = 0,
Suspended,
Revoked,
}
“AndRestored”案例将是那些IsActive == true
和InactiveReason != InactiveReason.None
的案例。
我得到的明显印象是你在这里过度工程。 :)
答案 1 :(得分:1)
选项3对我来说似乎是一个好主意。请记住,.NET枚举在设置为未定义的值时不会抛出异常,因此没有任何东西阻止用户说:
PrintDriversLicenseStatus(42);
...其中PrintDriversLicenseStatus
以DriversLicenseFlags
作为参数。
所以你需要让每个方法检查它的输入,以确保它们是一个定义的值。
另一个需要考虑的选项:创建一个特殊的内部枚举类,并将其转换为内部使用:
[Flags]
public enum DriversLicenseFlagsInternal
{
None = 0,
Suspended = 1 << 1,
Revoked = 1 << 2,
Restored = 1 << 3,
SuspendedAndRestored = Suspended | Restored,
RevokedAndRestored = Revoked | Restored,
}
[Flags]
internal enum DriversLicenseFlags
{
None = DriversLicenseFlagsInternal.None,
Suspended = DriversLicenseFlagsInternal.Suspended,
Revoked = DriversLicenseFlagsInternal.Revoked,
SuspendedAndRestored = DriversLicenseFlagsInternal.SuspendedAndRestored,
RevokedAndRestored = DriversLicenseFlagsInternal.RevokedAndRestored,
}
public void DoSomething(DriversLicenseFlags arg)
{
var argAsInternal = (DriversLicenseFlagsInternal) arg;
// or var argAsInternal = Util.CheckDefinedDriversLicense(arg);
}
不确定这是否更好,但可能感觉不那么苛刻。
答案 2 :(得分:0)
我会去标志并创建验证方法
[Flags]
public enum DriversLicenseFlags {
None,
Suspended,
Revoked,
Restored
}
public bool Validate(DriversLicenseFlags flags) {
if(flags.HasFlag(DriversLicenseFlags.Restored)) {
return
flags.HasFlag(DriversLicenseFlags.Revoked) ||
flags.HasFlag(DriversLicenseFlags.Suspended);
// Or throw an exception
}
return true;
}
如果你能忍受它违反微软的建议。我认为这是一个边缘案例。