Возможно ли создать общий метод побитового перечисления 'IsOptionSet()'?
-
06-07-2019 - |
Вопрос
Приведенный ниже код упрощает передачу набора HtmlParserOptions, а затем проверяет один параметр, чтобы увидеть, был ли он выбран.
[Flags]
public enum HtmlParserOptions
{
NotifyOpeningTags = 1,
NotifyClosingTags = 2,
NotifyText = 4,
NotifyEmptyText = 8
}
private bool IsOptionSet(HtmlParserOptions options, HtmlParserOptions singleOption)
{
return (options & singleOption) == singleOption;
}
Мой вопрос в том, возможно ли создать общую версию этого (я предполагаю, путем реализации интерфейса в свойствах метода), который будет работать с Любой перечисление с атрибутом Flags?
Решение
Редактировать:
Самый простой и приятный вариант - перейти на VS2010 Beta2 и использовать .NET 4 Перечисление.HasFlag способ.Команда фреймворка добавила много приятных дополнений к Enum, чтобы сделать их более удобными в использовании.
Оригинал (для текущего .NET):
Вы можете сделать это, передав Enum вместо generics:
static class EnumExtensions
{
private static bool IsSignedTypeCode(TypeCode code)
{
switch (code)
{
case TypeCode.Byte:
case TypeCode.UInt16:
case TypeCode.UInt32:
case TypeCode.UInt64:
return false;
default:
return true;
}
}
public static bool IsOptionSet(this Enum value, Enum option)
{
if (IsSignedTypeCode(value.GetTypeCode()))
{
long longVal = Convert.ToInt64(value);
long longOpt = Convert.ToInt64(option);
return (longVal & longOpt) == longOpt;
}
else
{
ulong longVal = Convert.ToUInt64(value);
ulong longOpt = Convert.ToUInt64(option);
return (longVal & longOpt) == longOpt;
}
}
}
Это работает отлично, вот так:
class Program
{
static void Main(string[] args)
{
HtmlParserOptions opt1 = HtmlParserOptions.NotifyText | HtmlParserOptions.NotifyEmptyText;
Console.WriteLine("Text: {0}", opt1.IsOptionSet(HtmlParserOptions.NotifyText));
Console.WriteLine("OpeningTags: {0}", opt1.IsOptionSet(HtmlParserOptions.NotifyOpeningTags));
Console.ReadKey();
}
}
Приведенные выше отпечатки:
Text: True
OpeningTags: False
Недостатком этого, однако, является то, что это не защищает вас от передачи двух разных типов перечислений в подпрограмму.Вы должны использовать его разумно.
Другие советы
Ну, вроде того.
Вы не можете добавить ограничение, чтобы убедиться, что аргумент type является перечислением "flags", и в обычном C # вы не можете добавить ограничение, чтобы убедиться, что это перечисление в первую очередь...но, немного поднапрягшись, вы можете заставить последнее сработать.Это допустимое ограничение в IL, но не в C #.Затем вам нужно будет немного поработать, чтобы заставить часть "и" работать в общем виде.
У меня есть проект под названием Непринужденная Мелодия который имеет несколько полезных методов расширения для перечислений, с помощью некоторой IL-перезаписи.В этом случае вы бы использовали:
if (options.HasAny(optionToTest))
или
if (options.HasAll(optionToTest))
в зависимости от того, как вы хотели бы справиться с ситуацией, когда optionToTest
на самом деле это несколько объединенных флагов.
В качестве альтернативы, дождитесь .NET 4.0 - the изменения в BCL включать в себя Enum.HasFlag
, который, я думаю, сделает то, что вы хотите.
public static bool IsOptionSet<T>(this T flags, T option) where T : struct
{
if(! flags is int) throw new ArgumentException("Flags must be int");
int opt = (int)(object)option;
int fl = (int)(object)flags;
return (fl & opt) == opt;
}
РЕДАКТИРОВАТЬ: как было отмечено в комментариях, это не будет работать, если enum - это что-то отличное от int (которое по умолчанию для enums). Вероятно, это должно быть названо как-то иначе, чтобы указать на это, но это, вероятно, «достаточно» для большинства случаев, если вам не нужен набор флагов с более чем 31 значением.