Как я могу узнать, что предметы в перечислении? - PullRequest
3 голосов
/ 20 сентября 2010

В этот вопрос , я использую оператор xor между enum с атрибутом [Flags] следующим образом:

[Flags]
enum QueryFlag
{
  None = 0x1,
  ByCustomer = 0x2,
  ByProduct = 0x4,
  ByDate = 0x8
}
QueryFlag flags = QueryFlag.ByCustomer | QueryFlag.ByProduct;

Чтобы добавить QueryFlag, конечно, мы должны использовать оператор |.

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

То есть, если флаги не изменены после удаления флага из флагов, мы знаем, что флаг не находится в флагах! Это кажется правильным, когда:

(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 универсальным для enum с атрибутом [Flags].

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

Возможно, невозможно ограничить T с помеченным атрибутом. Но даже если удалить это ограничение, я получаю ошибку компиляции, которая говорит operator ~ can't be applied to type T. Почему и как решить?

Ответы [ 2 ]

0 голосов
/ 20 сентября 2010

Ваша ошибка заключается в этом методе:

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

Возвращает true, если flags имеет любой (т. Е. хотя бы один ) из содержащихся флагов.в flag, но я думаю, что вы хотите все .

Он должен читать:

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

В качестве альтернативы, вы можете просто использовать Enum.HasFlag(), который делает именно это,Например:

QueryFlag qf = ...;
if (qf.HasFlag(QueryFlag.ByCustomer))
    // ...
0 голосов
/ 20 сентября 2010

Лучший способ может быть таким:

return (flags & mask) == mask;

Это вернет true, если все флаги, установленные в mask, установлены в flags.

return (flags & mask) != 0;

Это вернет true, если любой из флагов, установленных в mask, установлен в flags.

Что касается универсального метода, вы можете попробовать ограничить тип до struct. Это ограничивает T типом значения.

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