¿Alguien sabe una buena solución para la falta de una restricción genérica enum?

Lo que quiero hacer es algo como esto: tengo enumeraciones con valores combinados marcados.

public static class EnumExtension { public static bool IsSet( this T input, T matchTo ) where T:enum //the constraint I want that doesn't exist in C#3 { return (input & matchTo) != 0; } } 

Entonces podría hacer:

 MyEnum tester = MyEnum.FlagA | MyEnum.FlagB if( tester.IsSet( MyEnum.FlagA ) ) //act on flag a 

Desafortunadamente las restricciones de C # donde las restricciones no tienen restricción enum, solo clase y estructura. C # no ve las enumeraciones como estructuras (aunque sean tipos de valores), así que no puedo agregar tipos de extensión como este.

Alguien sabe una solución?

EDITAR: ahora está en vivo en la versión 0.0.0.2 de UnconstrainedMelody.

(Como solicité en mi blog sobre restricciones enum . He incluido los hechos básicos a continuación por el bien de una respuesta independiente.)

La mejor solución es esperar a que la incluya en UnconstrainedMelody 1 . Esta es una biblioteca que toma el código C # con restricciones “falsas” como

 where T : struct, IEnumConstraint 

y lo convierte en

 where T : struct, System.Enum 

a través de un paso posterior a la construcción.

No debería ser demasiado difícil escribir IsSet … aunque satisfacer las Int64 basadas en UInt64 y UInt64 en UInt64 podría ser la parte difícil. (Huelo algunos métodos de ayuda, básicamente, lo que me permite tratar cualquier enumeración de banderas como si tuviera un tipo base de UInt64 ).

¿Qué quieres que sea el comportamiento si llamas

 tester.IsSet(MyFlags.A | MyFlags.C) 

? ¿Debería verificar que todos los indicadores especificados estén configurados? Esa sería mi expectativa.

Trataré de hacer esto en el camino a casa esta noche … Espero tener un bombardeo rápido sobre métodos de enumeración útiles para hacer que la biblioteca scope un estándar utilizable rápidamente, y luego relajarme un poco.

EDITAR: No estoy seguro acerca de IsSet como un nombre, por cierto. Opciones:

  • Incluye
  • Contiene
  • HasFlag (o HasFlags)
  • IsSet (sin duda es una opción)

Pensamientos bienvenidos. Estoy seguro de que pasará un tiempo antes de que todo quede grabado …


1 o enviarlo como un parche, por supuesto …

Darren, eso funcionaría si los tipos fueran enumeraciones específicas: para que las enumeraciones generales funcionen, tienes que convertirlas en ints (o más probablemente uint) para hacer las operaciones matemáticas booleanas:

 public static bool IsSet( this Enum input, Enum matchTo ) { return ( Convert.ToUInt32( input ) & Convert.ToUInt32( matchTo ) ) != 0; } 

En realidad, es posible, con un truco feo. Sin embargo, no puede usarse para métodos de extensión.

 public abstract class Enums where Temp : class { public static TEnum Parse(string name) where TEnum : struct, Temp { return (TEnum)Enum.Parse(typeof(TEnum), name); } } public abstract class Enums : Enums { } Enums.IsSet("Local") 

Si lo desea, puede dar a Enums un constructor privado y una clase heredada abstracta anidada pública con Temp como Enum , para evitar versiones heredadas para no enumerados.

Puedes lograrlo usando IL Weaving and ExtraConstraints

Te permite escribir este código

 public class Sample { public void MethodWithDelegateConstraint<[DelegateConstraint] T> () { } public void MethodWithEnumConstraint<[EnumConstraint] T>() { } } 

Lo que se comstack

 public class Sample { public void MethodWithDelegateConstraint() where T: Delegate { } public void MethodWithEnumConstraint() where T: struct, Enum { } } 

A partir de C # 7.3, ahora hay una forma incorporada de agregar restricciones enum:

 public class UsingEnum where T : System.Enum { } 

fuente: https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/where-generic-type-constraint

Esto no responde a la pregunta original, pero ahora hay un método en .NET 4 llamado Enum.HasFlag que hace lo que estás tratando de hacer en tu ejemplo

La forma en que lo hago es poner una restricción estructural, luego verificar que T es una enumeración en tiempo de ejecución. Esto no elimina el problema por completo, pero lo reduce un poco

A partir de C # 7.3, puede usar la restricción Enum en tipos generics:

 public static TEnum Parse(string value) where TEnum : Enum { return (TEnum) Enum.Parse(typeof(TEnum), value); } 

Si desea utilizar una enumeración Nullable, debe abandonar la restricción de estructura orginial:

 public static TEnum? TryParse(string value) where TEnum : struct, Enum { if( Enum.TryParse(value, out TEnum res) ) return res; else return null; } 

Usando su código original, dentro del método también puede usar la reflexión para probar que T es una enumeración:

 public static class EnumExtension { public static bool IsSet( this T input, T matchTo ) { if (!typeof(T).IsEnum) { throw new ArgumentException("Must be an enum", "input"); } return (input & matchTo) != 0; } } 

Aquí hay un código que acabo de arreglar que parece funcionar como tú quieres sin tener que hacer nada demasiado loco. No está restringido solo a las enumeraciones configuradas como Indicadores, pero siempre puede haber un control puesto si es necesario.

 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."); } } 

si alguien necesita el IsSet genérico (creado fuera de la caja en la marcha podría mejorarse) y / o una cadena para la conversión Enum onfly (que usa EnumConstraint que se presenta a continuación):

  public class TestClass { } public struct TestStruct { } public enum TestEnum { e1, e2, e3 } public static class TestEnumConstraintExtenssion { public static bool IsSet(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(_this); //} //public static TestStruct ToTestStruct(this string _this) //{ // // #generates compile error (so no missuse) // return EnumConstraint.TryParse(_this); //} public static TestEnum ToTestEnum(this string _this) { // #enum type works just fine (coding constraint to Enum type) return EnumConstraint.TryParse(_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.e2); // you can specify explicite type TestStruct t; // #generates compile error (so no missuse) //bool b3 = t.IsSet(TestEnum.e1); } } 

Si alguien todavía necesita un ejemplo para crear una restricción de encoding Enum:

 using System; ///  /// would be same as EnumConstraint_T<Enum>Parse<EnumType>("Normal"), /// but writen like this it abuses constrain inheritence on System.Enum. ///  public class EnumConstraint : EnumConstraint_T { } ///  /// provides ability to constrain TEnum to System.Enum abusing constrain inheritence ///  /// should be System.Enum public abstract class EnumConstraint_T where TClass : class { public static TEnum Parse(string value) where TEnum : TClass { return (TEnum)Enum.Parse(typeof(TEnum), value); } public static bool TryParse(string value, out TEnum evalue) where TEnum : struct, TClass // struct is required to ignore non nullable type error { evalue = default(TEnum); return Enum.TryParse(value, out evalue); } public static TEnum TryParse(string value, TEnum defaultValue = default(TEnum)) where TEnum : struct, TClass // struct is required to ignore non nullable type error { Enum.TryParse(value, out defaultValue); return defaultValue; } public static TEnum Parse(string value, TEnum defaultValue = default(TEnum)) where TEnum : struct, TClass // struct is required to ignore non nullable type error { TEnum result; if (Enum.TryParse(value, out result)) return result; return defaultValue; } public static TEnum Parse(ushort value) { return (TEnum)(object)value; } public static sbyte to_i1(TEnum value) { return (sbyte)(object)Convert.ChangeType(value, typeof(sbyte)); } public static byte to_u1(TEnum value) { return (byte)(object)Convert.ChangeType(value, typeof(byte)); } public static short to_i2(TEnum value) { return (short)(object)Convert.ChangeType(value, typeof(short)); } public static ushort to_u2(TEnum value) { return (ushort)(object)Convert.ChangeType(value, typeof(ushort)); } public static int to_i4(TEnum value) { return (int)(object)Convert.ChangeType(value, typeof(int)); } public static uint to_u4(TEnum value) { return (uint)(object)Convert.ChangeType(value, typeof(uint)); } } 

Espero que esto ayude a alguien.

Solo quería agregar Enum como una restricción genérica.

Si bien esto es solo para un pequeño método de ayuda usando ExtraConstraints es demasiado para mí.

Decidí simplemente crear una restricción de struct y agregar una verificación en tiempo de ejecución para IsEnum . Para convertir una variable de T a Enum, la lanzo al objeto primero.

  public static Converter CreateConverter() where T : struct { if (!typeof(T).IsEnum) throw new ArgumentException("Given Type is not an Enum"); return new Converter(x => ((Enum)(object)x).GetEnumDescription()); }