Я хочу сделать примерно следующее: у меня есть перечисления с комбинированными помеченными значениями.
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 #, в котором ограничения не имеют ограничений перечисления, только class и struct. C # не видит перечисления как структуры (хотя они являются типами значений), поэтому я не могу добавлять такие типы расширений.
Кто-нибудь знает обходной путь?
struct
просто отлично.Ответы:
РЕДАКТИРОВАТЬ: теперь это работает в версии 0.0.0.2 UnconstrainedMelody.
(В соответствии с просьбой в моем сообщении в блоге об ограничениях перечисления . Я включил основные факты ниже для отдельного ответа.)
Лучшее решение - дождаться, пока я включу его в UnconstrainedMelody 1 . Это библиотека, которая использует код C # с «фальшивыми» ограничениями, такими как
where T : struct, IEnumConstraint
и превращает это в
where T : struct, System.Enum
через шаг после сборки.
Это не должно быть слишком сложно написать
IsSet
... хотя поддержка какInt64
-based, так иUInt64
-based флагов может быть сложной задачей. (Я чувствую появление некоторых вспомогательных методов, которые позволяют мне обрабатывать любые перечисления флагов, как если бы они имели базовый типUInt64
.)Каким будет поведение, если вы позвоните
? Должен ли он проверить, что все указанные флаги установлены? Это было бы моим ожиданием.
Я постараюсь сделать это по дороге домой сегодня вечером ... Я надеюсь быстро познакомиться с полезными методами перечисления, чтобы быстро довести библиотеку до приемлемого стандарта, а затем немного расслабиться.
РЕДАКТИРОВАТЬ:
IsSet
Кстати, я не уверен, что такое имя. Параметры:Мысли приветствуются. Я уверен, что пройдет какое-то время, прежде чем что-нибудь станет каменным ...
1 или, конечно, отправить как патч ...
источник
colors.HasAny(Colors.Red | Colors.Blue)
выглядит как очень читаемый код.=)
where T : System.Enum
. Об этом уже писали где-то в ветке; просто подумал, что повторю здесь.Начиная с C # 7.3, теперь есть встроенный способ добавления ограничений перечисления:
public class UsingEnum<T> where T : System.Enum { }
источник: https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/where-generic-type-constraint
источник
Даррен, это сработало бы, если бы типы были конкретными перечислениями - чтобы общие перечисления работали, вы должны преобразовать их в целые числа (или, что более вероятно, uint), чтобы выполнить логическую математику:
public static bool IsSet( this Enum input, Enum matchTo ) { return ( Convert.ToUInt32( input ) & Convert.ToUInt32( matchTo ) ) != 0; }
источник
Convert.ToUInt32
я больше нигде не нашел. AFAIK, это единственное достойное решение Pre-Net-4, которое также работает в VB. Кстати, еслиmatchTo
может быть несколько битов флага, замените!= 0
на== Convert.ToUInt32(matchTo)
.Convert.ToUInt32
при использовании перечисления будет использоватьсяConvert.ToUInt32(object)
перегрузка, что означает, что среда CLR сначала будет упаковывать эти значения, а затем передавать ихToUInt32
методу. В большинстве случаев это не имеет значения, но хорошо знать, что у вас будет довольно загруженный сборщик мусора, если вы будете использовать что-то подобное для анализа миллионов перечислений в секунду.На самом деле это возможно, но с уродливой уловкой. Однако его нельзя использовать для методов расширения.
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>
частный конструктор и общедоступный вложенный абстрактный унаследованный класс с помощьюTemp
asEnum
, чтобы предотвратить унаследованные версии для не перечислений.источник
Вы можете добиться этого с помощью IL Weaving и ExtraConstraints.
Позволяет написать этот код
public class Sample { public void MethodWithDelegateConstraint<[DelegateConstraint] T> () { } public void MethodWithEnumConstraint<[EnumConstraint] T>() { } }
Что компилируется
public class Sample { public void MethodWithDelegateConstraint<T>() where T: Delegate { } public void MethodWithEnumConstraint<T>() where T: struct, Enum { } }
источник
Начиная с C # 7.3, вы можете использовать ограничение Enum для универсальных типов:
public static TEnum Parse<TEnum>(string value) where TEnum : Enum { return (TEnum) Enum.Parse(typeof(TEnum), value); }
Если вы хотите использовать перечисление Nullable, вы должны оставить ограничение исходной структуры:
public static TEnum? TryParse<TEnum>(string value) where TEnum : struct, Enum { if( Enum.TryParse(value, out TEnum res) ) return res; else return null; }
источник
Это не отвечает на исходный вопрос, но теперь в .NET 4 есть метод Enum.HasFlag, который делает то, что вы пытаетесь сделать в своем примере.
источник
flag
. .NET 4.0 исполнилось пять лет.Я делаю это так: накладываю структурное ограничение, а затем проверяю, является ли T перечислением во время выполнения. Это не устраняет проблему полностью, но несколько снижает ее.
источник
Используя исходный код, внутри метода вы также можете использовать отражение, чтобы проверить, является ли 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; } }
источник
Вот код, который я только что написал, который, кажется, работает так, как вы хотите, без необходимости делать что-то слишком сумасшедшее. Это не ограничивается только перечислениями, установленными как флаги, но при необходимости всегда может быть поставлена проверка.
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."); } }
источник
если кому-то нужен общий IsSet (созданный из коробки на лету, который можно улучшить), и / или строка для преобразования Enum onfly (которое использует EnumConstraint, представленный ниже):
public class TestClass { } public struct TestStruct { } public enum TestEnum { e1, e2, e3 } public static class TestEnumConstraintExtenssion { public static bool IsSet<TEnum>(this TEnum _this, TEnum flag) where TEnum : struct { return (((uint)Convert.ChangeType(_this, typeof(uint))) & ((uint)Convert.ChangeType(flag, typeof(uint)))) == ((uint)Convert.ChangeType(flag, typeof(uint))); } //public static TestClass ToTestClass(this string _this) //{ // // #generates compile error (so no missuse) // return EnumConstraint.TryParse<TestClass>(_this); //} //public static TestStruct ToTestStruct(this string _this) //{ // // #generates compile error (so no missuse) // return EnumConstraint.TryParse<TestStruct>(_this); //} public static TestEnum ToTestEnum(this string _this) { // #enum type works just fine (coding constraint to Enum type) return EnumConstraint.TryParse<TestEnum>(_this); } public static void TestAll() { TestEnum t1 = "e3".ToTestEnum(); TestEnum t2 = "e2".ToTestEnum(); TestEnum t3 = "non existing".ToTestEnum(); // default(TestEnum) for non existing bool b1 = t3.IsSet(TestEnum.e1); // you can ommit type bool b2 = t3.IsSet<TestEnum>(TestEnum.e2); // you can specify explicite type TestStruct t; // #generates compile error (so no missuse) //bool b3 = t.IsSet<TestEnum>(TestEnum.e1); } }
Если кому-то все еще нужен пример для создания ограничения кодирования Enum:
using System; /// <summary> /// would be same as EnumConstraint_T<Enum>Parse<EnumType>("Normal"), /// but writen like this it abuses constrain inheritence on System.Enum. /// </summary> public class EnumConstraint : EnumConstraint_T<Enum> { } /// <summary> /// provides ability to constrain TEnum to System.Enum abusing constrain inheritence /// </summary> /// <typeparam name="TClass">should be System.Enum</typeparam> public abstract class EnumConstraint_T<TClass> where TClass : class { public static TEnum Parse<TEnum>(string value) where TEnum : TClass { return (TEnum)Enum.Parse(typeof(TEnum), value); } public static bool TryParse<TEnum>(string value, out TEnum evalue) where TEnum : struct, TClass // struct is required to ignore non nullable type error { evalue = default(TEnum); return Enum.TryParse<TEnum>(value, out evalue); } public static TEnum TryParse<TEnum>(string value, TEnum defaultValue = default(TEnum)) where TEnum : struct, TClass // struct is required to ignore non nullable type error { Enum.TryParse<TEnum>(value, out defaultValue); return defaultValue; } public static TEnum Parse<TEnum>(string value, TEnum defaultValue = default(TEnum)) where TEnum : struct, TClass // struct is required to ignore non nullable type error { TEnum result; if (Enum.TryParse<TEnum>(value, out result)) return result; return defaultValue; } public static TEnum Parse<TEnum>(ushort value) { return (TEnum)(object)value; } public static sbyte to_i1<TEnum>(TEnum value) { return (sbyte)(object)Convert.ChangeType(value, typeof(sbyte)); } public static byte to_u1<TEnum>(TEnum value) { return (byte)(object)Convert.ChangeType(value, typeof(byte)); } public static short to_i2<TEnum>(TEnum value) { return (short)(object)Convert.ChangeType(value, typeof(short)); } public static ushort to_u2<TEnum>(TEnum value) { return (ushort)(object)Convert.ChangeType(value, typeof(ushort)); } public static int to_i4<TEnum>(TEnum value) { return (int)(object)Convert.ChangeType(value, typeof(int)); } public static uint to_u4<TEnum>(TEnum value) { return (uint)(object)Convert.ChangeType(value, typeof(uint)); } }
надеюсь, это кому-то поможет.
источник
Я просто хотел добавить Enum в качестве общего ограничения.
Хотя это только для крошечного вспомогательного метода, использующего
ExtraConstraints
для меня слишком много накладных расходов.Я решил просто создать
struct
ограничение и добавить проверку времени выполнения дляIsEnum
. Для преобразования переменной из T в Enum я сначала привел ее к объекту.public static Converter<T, string> CreateConverter<T>() where T : struct { if (!typeof(T).IsEnum) throw new ArgumentException("Given Type is not an Enum"); return new Converter<T, string>(x => ((Enum)(object)x).GetEnumDescription()); }
источник