【问题标题】:Is an empty flag a bad habit?空旗是坏习惯吗?
【发布时间】:2016-03-07 09:48:35
【问题描述】:

几天前我遇到了一个非常愚蠢的错误。这是由我从第三方库获得的这个枚举引起的:

[Flags]
public enum MyStatus
{
    OKResponse = 0,
    ResponseTooBig = 1,
    ErrorMessage = 2,
    NoResponse = 4,
    ...
}

我习惯用这种方式检查标志:

if ((status & MyStatus.OKResponse) != 0) {...}

但它不适用于MyStatus.OKResponse,因为它为零。它根本不是一面旗帜,它是所有旗帜的缺失。当然,当我发现 bug 时,我意识到 OKResponse 是唯一的非错误状态,所以它的真正意思是“没有错误,没有标志”。但是,我真的不觉得很明显。

将 0 定义为 flags 枚举中的值之一是一个坏习惯吗?推荐的方法是什么?检查标志的最佳方法是什么,也可以与“无标志”标志一起使用?

【问题讨论】:

  • 这不仅不是一个坏习惯,而且恰恰相反——不定义零值是不好的。这是因为枚举的默认值为 0。惯例是调用它None。此外,当使用[Flags] 时,约定是枚举名称的复数形式。看起来你的枚举应该被称为Errors
  • 为此使用标志枚举似乎是错误的。您是否有一个由多个位表示的状态?
  • @Steve 是的,我不得不把这个例子匿名化一点......第一个是 OKResponse,其余的是一些错误标志,不止一个可能。使用标志是可以的,真的。
  • 请注意,如果您将None = 0 添加到枚举中,您的检查将变为if (status != Errors.None),这非常易读。 (而且我认为您需要添加一个 None 成员并将枚举重命名为遵循 the enum naming convention。)
  • @Jodrell 是的,当然。但我必须知道MyStatus.OKResponse 是一个特殊的值......我真的应该检查我得到的所有库中的所有枚举吗?

标签: c# enums flags anti-patterns


【解决方案1】:

将 0 定义为 flags 枚举中的值之一是一个坏习惯吗?

不,相反,正如 cmets 所说,通常使用0 作为给定标志的值,如果没有为第一个给定值分配不同的值,这就是枚举的默认值.正如其他人在 cmets 中所说,使用Enum.None 作为枚举的第一个值也很常见,这样可以让其他阅读代码的人更清楚您的意图。

推荐的方式是什么?

没有一种方法可以做到,但我通常喜欢使用简洁的Enum.HasFlag方法:

void Main()
{
    var status = MyStatus.ResponseTooBig | MyStatus.NoResponse;
    if (status.Equals(MyStatus.OKResponse))
        Console.WriteLine("Status is OKResponse");
    else 
        Console.WriteLine($"Has NoResponse?: {status.HasFlag(MyStatus.NoResponse)}");
}

【讨论】:

  • 问题是status.HasFlag(MyStatus.OKResponse)总是为真,所以在这种情况下不起作用...见stackoverflow.com/questions/15436616/…
  • @vojta 解决方案在同一篇文章中: 如果 flag 的基础值为零,则该方法返回 true。如果此行为不可取,您可以使用 Equals 方法测试是否与零相等,并仅在 flag 的基础值非零时调用 HasFlag,如下例所示。
  • 是的,我知道。但是你的例子写了true,这不是很好——有一个ResponseTooBig错误标志,对吧?
【解决方案2】:

虽然在枚举中实际定义一个0 值是个好主意,但表示OK 是一个非常糟糕的主意,因为0 是默认值,它是唯一的特殊整数隐式可转换为枚举,因此它可以轻松潜入:

MyStatus status = 0;

因此,0 值的实际定义不是问题,但最好让它代表 None 代表 Flags 枚举或 Invalid 值代表普通枚举。

此外,Flags 枚举应在成员可以组合时使用,但从您的示例来看,它们似乎是互斥的。

所以我会说枚举的设计很糟糕,因为事实上,任何响应都有一个 OK 标志,这对于 Flags 枚举肯定是一个错误,如果你不能更改定义,唯一的方法将明确检查状态是否等于OKResponse

if (status == MyStatus.OKResponse)

【讨论】:

    猜你喜欢
    • 2023-01-05
    • 1970-01-01
    • 1970-01-01
    • 2023-01-12
    • 2010-10-16
    • 2013-07-17
    • 2011-11-04
    • 2016-03-16
    • 2015-09-06
    相关资源
    最近更新 更多