Každý, kdo vědí o dobré řešení pro nedostatek k výčtu generických omezení?

hlasů
79

To, co chci udělat, je něco takového: Mám výčty s kombinovanými označených hodnot.

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

A tak bych mohl udělat:

MyEnum tester = MyEnum.FlagA | MyEnum.FlagB

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

Bohužel C # 's generic kde omezení nemají omezení výčtu, pouze třídu a struct. C # nevidí výčty jako structs (přestože jsou typy hodnot), takže nemohu přidat typy rozšíření takhle.

Každý, kdo vědí o řešení?

Položena 10/08/2008 v 18:14
zdroj uživatelem
V jiných jazycích...                            


12 odpovědí

hlasů
46

EDIT: To je nyní žije ve verzi 0.0.0.2 ze UnconstrainedMelody.

(V souladu s požadavkem na mém blogu o výčtu překážek . Já jsem součástí základní fakta níže kvůli samostatnou odpověď.)

Nejlepším řešením je, aby čekat, až ji zahrnout do UnconstrainedMelody 1 . To je knihovna, která má kód C # s „falešnými“ omezeními, jako jsou

where T : struct, IEnumConstraint

a stáčí se do

where T : struct, System.Enum

přes postbuild kroku.

To by nemělo být příliš těžké psát IsSet... i když obstarávat obě Int64bázi a UInt64na bázi vlajek může být choulostivé části. (Cítím některé pomocné metody přicházejí v podstatě umožňuje, abych se k léčbě jakékoliv příznaky enum, jako by šlo o základní typ UInt64.)

Co by chcete, aby se chování, které se v případě, že voláš

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

? By mělo zkontrolovat, zda jsou všechny jsou nastaveny uvedené příznaky? Že by byla moje očekávání.

Pokusím se to na cestě domů dnes v noci ... Doufám, že se rychle nálety na užitečných metod výčtu dostat knihovnu až do použitelného standardu rychle, pak trochu odpočinout.

EDIT: Nejsem si jistý IsSet, jak na jméno, mimochodem. Volby:

  • Zahrnuje
  • obsahuje
  • HasFlag (nebo HasFlags)
  • Isset (to je určitě možnost)

Myšlenky vítány. Jsem si jistý, že to bude chvíli trvat, než všechno je vytesáno do kamene stejně ...


1 nebo předložit ji jako záplata, samozřejmě ...

Odpovězeno 11/09/2009 v 10:12
zdroj uživatelem

hlasů
16

Darren, který bude fungovat v případě, že typy byly specifické výčty - pro všeobecné výčtů do práce máte uvrhli do ints (nebo spíše uint) a provést boolean matematiku:

public static bool IsSet( this Enum input, Enum matchTo )
{
    return ( Convert.ToUInt32( input ) & Convert.ToUInt32( matchTo ) ) != 0;
}
Odpovězeno 10/08/2008 v 23:53
zdroj uživatelem

hlasů
9

Ve skutečnosti je to možné, s ošklivý trik. Nicméně, nemůže být použita k rozšíření metody.

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")

Pokud chcete, můžete si dát Enums<Temp>vlastní konstruktor a veřejný vnořené abstraktní dědičnou třídu Tempas Enum, aby se zabránilo zděděné verze pro nekuřáky výčty.

Odpovězeno 13/09/2009 v 03:41
zdroj uživatelem

hlasů
8

Můžete dosáhnout tohoto pomocí IL tkaní a ExtraConstraints

Umožňuje psát tento kód

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

Co dostane sestavují

public class Sample
{
    public void MethodWithDelegateConstraint<T>() where T: Delegate
    {
    }

    public void MethodWithEnumConstraint<T>() where T: struct, Enum
    {
    }
}
Odpovězeno 20/07/2012 v 08:11
zdroj uživatelem

hlasů
5

Jak C # 7.3, tam je nyní vestavěný způsob, jak přidat enum omezení:

public class UsingEnum<T> where T : System.Enum { }

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

Odpovězeno 11/05/2018 v 09:33
zdroj uživatelem

hlasů
4

To není odpověď na původní otázku, ale nyní existuje metoda .NET 4, nazvaný Enum.HasFlag který dělá to, co se snažíte dělat ve svém příkladu

Odpovězeno 20/11/2009 v 12:08
zdroj uživatelem

hlasů
3

Jak jsem to udělat, je dát struct omezení, pak zkontrolujte, zda T je výčet za běhu. To není problém odstranit úplně, ale to ji snížit poněkud

Odpovězeno 27/07/2009 v 15:02
zdroj uživatelem

hlasů
1

Zde je několik kód, který jsem právě udělal up, který vypadá, že fungují jako chcete, aniž byste museli dělat cokoliv příliš šílené. To není omezeno pouze na výčty nastavených jako Flags, ale tam může být vždy zkontrolujte, zda to bude nutné vložit.

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.");
    }
}
Odpovězeno 13/09/2009 v 04:57
zdroj uživatelem

hlasů
1

Pomocí původního kódu uvnitř metody můžete také použít odraz otestovat, že T je výčtu:

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;
    }
}
Odpovězeno 17/08/2008 v 05:51
zdroj uživatelem

hlasů
0

Jak C # 7.3, můžete použít omezení Enum o generických typů:

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

Chcete-li použít NULLABLE výčet, je třeba ponechat orginial struct omezení:

public static TEnum? TryParse<TEnum>(string value) where TEnum : struct, Enum
{
    if( Enum.TryParse(value, out TEnum res) )
        return res;
    else
        return null;
}
Odpovězeno 18/05/2018 v 13:04
zdroj uživatelem

hlasů
0

pokud někdo potřebuje generické isset (vytvořené z krabice na fly by bylo možné zlepšit v), a nebo řetězec Enum onfly konverzi (který používá EnumConstraint níže):

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

    }

  }

Pokud se někdo stále potřebuje příklad horké vytvořit Enum kódování omezení:

using System;

/// <summary>
/// would be same as EnumConstraint_T&lt;Enum>Parse&lt;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));
  }

}

Doufám, že to někomu pomůže.

Odpovězeno 22/12/2016 v 08:42
zdroj uživatelem

hlasů
0

Jen jsem chtěl přidat Enum jako obecný omezení.

I když je to jen na malou pomocnou metodu při použití ExtraConstraintsje trochu moc režie pro mě.

Rozhodl jsem se prostě jen vytvořit structtlak a přidat kontrolu runtime pro IsEnum. Pro konverzi proměnnou z T na eNUM I vrci objekt jako první.

    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());
    }
Odpovězeno 18/03/2016 v 07:49
zdroj uživatelem

Cookies help us deliver our services. By using our services, you agree to our use of cookies. Learn more