如何确定一个项目是否在枚举中?

3
这个问题中,我使用异或运算符来处理带有[Flags]属性的enum,具体如下:
[Flags]
enum QueryFlag
{
  None = 0x1,
  ByCustomer = 0x2,
  ByProduct = 0x4,
  ByDate = 0x8
}
QueryFlag flags = QueryFlag.ByCustomer | QueryFlag.ByProduct;

为添加一个查询标记,我们应该使用 | 运算符。
flags |= QueryFlag.ByDate;

要移除其中一个,我有一种不同于丹·陶的答案的方法。我使用的是:

flags ^= QueryFlag.ByProduct;

当他在使用时:

flags &= ~QueryFlag.ByProduct;

显然,他的回答是正确且易于理解的。一开始我以为自己犯了错误,但经过深思熟虑后,我明白了:

a,b         a^b         a&(~b)
0,0          0           0
0,1          1           0   //the difference
1,0          1           1
1,1          0           0

现在我知道了我的错误。当您尝试删除一个不存在的项目时,^是错误的。
QueryFlag q = QueryFlag.ByCustomer | QueryFlag.ByDate;
//try to remove QueryFlag.ByProduct which doesn't exist in q
q ^ QueryFlag.ByProduct    //equals to add ByProduct to q, wrong!
q & (~QueryFlag.ByProduct) // q isn't changed, remain the original value. correct!

但是我有另一个问题:如何知道q是否包含一个项目?根据丹·陶的答案,我编写了一个扩展:

public static bool Contains(this QueryFlag flags, QueryFlag flag)
{
   return (flags & (~flag)) != flags;
}

也就是说,如果在从flags中移除flag后,flags没有发生变化,那么我们知道flag不在flags中!当以下情况时,这个结论是正确的:
(QueryFlag.ByProduct | QueryFlag.ByDate).Contains(QueryFlag.None)   //false
(QueryFlag.ByProduct | QueryFlag.ByDate).Contains(QueryFlag.ByDate)  //true

但事实上:
(QueryFlag.ByProduct | QueryFlag.ByDate).Contains(QueryFlag.ByDate | QueryFlag.ByCustomer) //true, but I suppose it's false

我知道它为什么是错误的,我该如何改进呢?这是第一个问题。 第二个问题:我想使.Contains适用于更多带有[Flags]属性的enum

public static bool Contains<T>(this T flags, T flag) where T : Enum//with [Flags]
{
    return (flags & (~flag)) != flags;
}

可能无法使用标记属性来约束 T。但是,即使我删除此约束,仍会出现编译错误,提示“operator ~ 无法应用于类型 T”。为什么会出现这种情况,如何解决?


你想让.Contains(QueryFlag.ByDate | QueryFlag.ByCustomer)返回true,如果其中一个标志被设置,还是只有两个标志都被设置时才返回true? - Andrew Cooper
2个回答

0
可能有更好的方法:
return (flags & mask) == mask;

如果mask中设置的所有标志都在flags中设置,则返回true。

return (flags & mask) != 0;

如果在mask中设置的任何标志在flags中设置,则将返回true。

至于通用方法,您可以尝试将类型限制为struct。这会将T限制为值类型。

public static bool Contains<T>(this T flags, T mask) where T : struct
{
    return (flags & mask) == mask;
}

现在我理解了你对我的第一个问题的两个答案。非常感谢。然而,关于第二个问题,写约束 where T:int 是不正确的,因为 int 不能成为 where 的约束条件。 - Cheng Chen
真的。我还在努力理解类型约束。似乎你可以将结构体指定为约束条件,这将限制T为值类型。请参见编辑后的答案。 - Andrew Cooper
答案仍然错误。您不能在 T 类型上使用 &==。我可以谦虚地建议您在发布代码之前尝试编译它吗? - Timwi

0

你的错误在于这个方法:

public static bool Contains(this QueryFlag flags, QueryFlag flag)
{
   return (flags & (~flag)) != flags;
}

每当flags包含flag中的任意一个(即至少一个)标志时,它返回true,但我认为您想要全部所以应该这样写:

正确的写法应该是:

public static bool Contains(this QueryFlag flags, QueryFlag flag)
{
   return (flags & flag) == flag;
}

或者,您可以直接使用Enum.HasFlag()来实现这一点。例如:

QueryFlag qf = ...;
if (qf.HasFlag(QueryFlag.ByCustomer))
    // ...

哦!我不知道HasFlag方法!那正是我想要的。谢谢。 - Cheng Chen

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