То, что я хочу сделать, является чем-то вроде этого: у Меня есть перечисления с объединенными отмеченными значениями.
public static class EnumExtension
{
public static bool IsSet<T>( this T input, T matchTo )
where T:enum //the constraint I want that doesn't exist in C#3
{
return (input & matchTo) != 0;
}
}
Таким образом я мог сделать:
MyEnum tester = MyEnum.FlagA | MyEnum.FlagB
if( tester.IsSet( MyEnum.FlagA ) )
//act on flag a
К сожалению, дженерик C#, где ограничения не имеют никакого перечислимого ограничения, только класс и структура. C# не рассматривает перечисления как структуры (даже при том, что они - типы значения), таким образом, я не могу добавить дополнительные типы как это.
Кто-либо знает обходное решение?
РЕДАКТИРОВАТЬ: Теперь он доступен в версии 0.0.0.2 UnconstrainedMelody.
(В соответствии с просьбой в моем сообщении в блоге об ограничениях перечисления . приведенные ниже факты для отдельного ответа.)
Лучшее решение - дождаться, когда я включу его в UnconstrainedMelody 1 . Это библиотека, которая берет код C # с «фальшивыми» ограничениями, такими как
where T : struct, IEnumConstraint
, и превращает его в
where T : struct, System.Enum
на этапе пост-сборки.
Не должно быть слишком сложно написать IsSet
. .. хотя поддержка как Int64
, так и UInt64
флагов может быть сложной задачей. (Я чувствую появление некоторых вспомогательных методов, которые позволяют мне обрабатывать любые перечисления флагов, как если бы они имели базовый тип UInt64
. )
Каким будет поведение, если вы позвоните
tester.IsSet(MyFlags.A | MyFlags.C)
? Должен ли он проверить, что установлены все указанные флаги? Это было бы моим ожиданием.
Я постараюсь сделать это по дороге домой сегодня вечером ... Я надеюсь быстро найти полезные методы перечисления, чтобы быстро довести библиотеку до пригодного для использования стандарта, а затем расслабьтесь немного.
РЕДАКТИРОВАТЬ: Кстати, я не уверен насчет имени IsSet
. Параметры:
Мысли приветствуются. Я уверен, что пройдет некоторое время, прежде чем что-нибудь будет высечено в камне ...
1 или, конечно, отправьте это как патч ...
Я попробую сделать это по дороге домой сегодня вечером ... Я надеюсь быстро познакомиться с полезными методами перечисления, чтобы быстро довести библиотеку до удобного стандарта, а затем немного расслабиться. РЕДАКТИРОВАТЬ: Я кстати, не уверен насчет имени IsSet
. Параметры:
Мысли приветствуются. Я уверен, что пройдет некоторое время, прежде чем что-нибудь будет высечено в камне ...
1 или, конечно, отправьте это как патч ...
Я попробую сделать это по дороге домой сегодня вечером ... Я надеюсь быстро познакомиться с полезными методами перечисления, чтобы быстро довести библиотеку до удобного стандарта, а затем немного расслабиться. РЕДАКТИРОВАТЬ: Я кстати, не уверен насчет имени IsSet
. Параметры:
Мысли приветствуются. Я уверен, что пройдет некоторое время, прежде чем что-нибудь будет высечено в камне ...
1 или, конечно, отправьте это как патч ...
s конечно вариант)Мысли приветствуются. Я уверен, что пройдет некоторое время, прежде чем что-нибудь будет высечено в камне ...
1 или, конечно, отправьте это как патч ...
s конечно вариант)Мысли приветствуются. Я уверен, что пройдет некоторое время, прежде чем что-нибудь будет высечено в камне ...
1 или, конечно, отправьте это как патч ...
Darren, который работал бы, если бы типы были определенными перечислениями - для общих перечислений для работы, необходимо бросить их к ints (или более вероятно uint), чтобы сделать булеву математику:
public static bool IsSet( this Enum input, Enum matchTo )
{
return ( Convert.ToUInt32( input ) & Convert.ToUInt32( matchTo ) ) != 0;
}
Используя Ваш исходный код, в методе можно также использовать отражение для тестирования этого, T является перечислением:
public static class EnumExtension
{
public static bool IsSet<T>( this T input, T matchTo )
{
if (!typeof(T).IsEnum)
{
throw new ArgumentException("Must be an enum", "input");
}
return (input & matchTo) != 0;
}
}
Как я это делаю, я устанавливаю ограничение структуры, а затем проверяю, является ли T перечислением во время выполнения. Это не устраняет проблему полностью, но несколько снижает ее
Вот код, который я только что сделал это, похоже, работает так, как вы хотите, без необходимости делать что-то слишком сумасшедшее. Это не ограничивается только перечислениями, установленными как флаги, но всегда можно поставить проверку, если потребуется.
public static class EnumExtensions
{
public static bool ContainsFlag(this Enum source, Enum flag)
{
var sourceValue = ToUInt64(source);
var flagValue = ToUInt64(flag);
return (sourceValue & flagValue) == flagValue;
}
public static bool ContainsAnyFlag(this Enum source, params Enum[] flags)
{
var sourceValue = ToUInt64(source);
foreach (var flag in flags)
{
var flagValue = ToUInt64(flag);
if ((sourceValue & flagValue) == flagValue)
{
return true;
}
}
return false;
}
// found in the Enum class as an internal method
private static ulong ToUInt64(object value)
{
switch (Convert.GetTypeCode(value))
{
case TypeCode.SByte:
case TypeCode.Int16:
case TypeCode.Int32:
case TypeCode.Int64:
return (ulong)Convert.ToInt64(value, CultureInfo.InvariantCulture);
case TypeCode.Byte:
case TypeCode.UInt16:
case TypeCode.UInt32:
case TypeCode.UInt64:
return Convert.ToUInt64(value, CultureInfo.InvariantCulture);
}
throw new InvalidOperationException("Unknown enum type.");
}
}
На самом деле, это возможно, с уродливой уловкой. Однако его нельзя использовать для методов расширения.
public abstract class Enums<Temp> where Temp : class {
public static TEnum Parse<TEnum>(string name) where TEnum : struct, Temp {
return (TEnum)Enum.Parse(typeof(TEnum), name);
}
}
public abstract class Enums : Enums<Enum> { }
Enums.IsSet<DateTimeKind>("Local")
При желании вы можете предоставить Enums
частный конструктор и открытый вложенный абстрактный унаследованный класс с Temp
в качестве Enum
, чтобы предотвратить унаследованные версии для не перечислений.
Это не отвечает на исходный вопрос, но теперь в .NET 4 есть метод под названием Enum.HasFlag , который делает то, что вы пытаетесь сделать в своем примере.