C#中枚举类型的私有值

6
我将创建一个在C#中的标志枚举,类似于以下代码:
    [Flags]
    public enum DriversLicenseFlags
    {
        None = 0,

        Suspended = 1 << 1,
        Revoked   = 1 << 2,

        Restored  = 1 << 3,
        SuspendedAndRestored = Suspended | Restored,
        RevokedAndRestored   = Revoked   | Restored,
    }

以下是我的意图:

  • SuspendedRevoked 是独特的状态,可以但不一定会导致恢复。
  • 只有当用户也被 SuspendedRevoked(或两者都)时,才应该允许 Restored。重要的是要具体跟踪哪个事件是恢复的前提条件。
  • 许可证可以同时被 SuspendedRevoked(和 Restored

此外,我试图遵循 MSDN 的 Designing Flags Enumerations 中提出的建议,包括:

  • 考虑为常用的标志组合提供特殊的枚举值。
    • SuspendedAndRestoredRevokedAndRestored 都是常见的组合。
  • 避免在某些值的组合无效时创建标志枚举。
    • 这是我的问题,因为只有在至少设置了一个 SuspendedRevoked 时,Restored 才是有效的。

理想情况下,我希望在枚举中为 Restored 提供一个值,以便在内部使用,但只能通过某些有效的组合公开设置。不幸的是,internal 不是枚举值的有效修饰符。

我想到了几个替代方案,但每个方案似乎都有缺点:

  1. Keep Restored as a public value, note the limitations in comments and do a precondition check for invalid combinations on public APIs.

    This would work, and is likely the solution I will go with. However, it seems like their should be a cleaner solution.

  2. Use an enhanced, java-like enum as described here and define Restored as internal static.

    This would also work, but feels like overkill because I don't need any of the other functionality at this point.

  3. Don't define Restored as a value, but reserve the value for OR'ing, and for checking the value in consuming methods. i.e.:

    internal const int RestoredFlag = 1 << 3;
    [Flags]
    public enum DriversLicenseFlags
    {
        None = 0,
    
        Suspended = 1 << 1,
        Revoked   = 1 << 2,
    
        SuspendedAndRestored = Suspended | RestoredFlag,
        RevokedAndRestored   = Revoked   | RestoredFlag,
    }
    

对我来说,这种方法感觉很不专业,无论是在定义方式上还是内部使用上。

3个回答

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 == trueInactiveReason != InactiveReason.None 的情况。
我有明显的感觉你在这里过度工程化了。 :)

你忘记了 SuspendedAndRevokedSuspendedAndRevokedAndRestored - Jan
他要求的是:只有在用户被“暂停”或“吊销”(或两者都有)的情况下才能进行“恢复”。 - Jan
1
@Jan:我认为你误解了OP的意图。请注意他也没有定义那些值。 - Jon
好的观点。在我的情况下,被暂停和吊销(以及恢复)同时存在也是有效的。在这种情况下,如果没有暴露“已恢复”状态,可能会更加混乱。 - Scott Wegner
@Jon,我喜欢将其拆分为两个单独的变量,用于表示“活动”状态和不活动原因。关于过度工程化,是的,绝对没错 :) - Scott Wegner
显示剩余2条评论

2

在我看来,选项3似乎是个不错的想法。请记住,当.NET枚举被设置为未定义的值时,它们不会抛出异常,因此没有任何阻止您的用户这样说的东西:

PrintDriversLicenseStatus(42);

...其中PrintDriversLicenseStatusDriversLicenseFlags作为其参数。

因此,您需要检查每个方法的输入,以确保它们是定义的值。

编辑

另一个选项供您考虑:创建一个特殊的内部枚举类,并将参数强制转换为该类以供内部使用:

[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);
}

不确定这是否更好,但可能会感觉不那么hacky。


当然,我不能保证我的用户不会明确地做出奇怪/恶意的事情。但是,我希望我的设计能够阻止这种行为。 :) - Scott Wegner
@Scott Wegner:这就是为什么我认为选项3是最好的原因。它有效地向用户呈现了逻辑选项,而不会走极端。创建一些内部实用方法来帮助检查驾驶执照是否已恢复等,以便所有的hackiness都存在于单个类中。 - StriplingWarrior

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;
}

如果您可以接受违反微软的建议,我认为这是一个边缘情况。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接