Pergunta

O que eu quero fazer é algo como isto:Eu tenho enums com o combinado sinalizado valores.

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

Então eu poderia fazer:

MyEnum tester = MyEnum.FlagA | MyEnum.FlagB

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

Infelizmente, C#'s genéricos, na qual as restrições não têm nenhum enum restrição, apenas a classe e estrutura.C# não vê as enumerações como estruturas (mesmo que eles são tipos de valor) então eu não posso adicionar uma extensão de tipos como este.

Alguém sabe uma solução?

Foi útil?

Solução

EDITAR:Este é agora ao vivo em versão 0.0.0.2 de UnconstrainedMelody.

(Conforme solicitado no meu post no blog sobre enum restrições.Eu incluí os fatos básicos abaixo por causa de um autônomo de resposta.)

A melhor solução é esperar para me incluir no UnconstrainedMelody1.Esta é uma biblioteca que leva o código C# com "falso" restrições tais como

where T : struct, IEnumConstraint

e o transforma em

where T : struct, System.Enum

através de um postbuild passo.

Não deve ser muito difícil escrever IsSet...apesar de catering para tanto Int64e com base em UInt64baseado no sinalizadores poderia ser a parte complicada.(Sinto o cheiro de alguns métodos auxiliares chegando, basicamente, o que me permite tratar qualquer bandeiras enum como se ele tivesse um tipo de base de UInt64.)

O que você deseja que o comportamento a ser se chamado

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

?Deve verificar que todos o especificado flags estão definidas?Essa seria a minha expectativa.

Eu vou tentar fazer isso em casa hoje à noite...Eu estou esperando uma rápida blitz no úteis enum métodos para obter a biblioteca até utilizável padrão rapidamente, em seguida, relaxe um pouco.

EDITAR:Eu não tenho certeza sobre IsSet como um nome, por sinal.Opções:

  • Inclui
  • Contém
  • HasFlag (ou HasFlags)
  • IsSet (é certamente uma opção)

Pensamentos de boas vindas.Eu tenho certeza que ele vai ser um tempo antes de qualquer coisa é, em pedra, de qualquer forma...


1 ou enviá-lo como um patch, é claro...

Outras dicas

Darren, que funcionaria se a tipos específicos de enumerações - geral enumerações para funcionar você tem que lançá-los para ints (ou, mais provavelmente uint) para fazer o boolean matemática:

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

Como de C# 7.3, há agora uma maneira interna para adicionar enum restrições:

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

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

Na verdade, é possível, com um feio truque.No entanto, ele não pode ser usado para métodos de extensão.

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

Se você quiser, você pode dar Enums<Temp> um construtor particular e uma pública aninhadas abstratos herdados da classe com Temp como Enum, para evitar que herdou versões para utilização não-enums.

Você pode conseguir isso usando o IL Tecelagem e ExtraConstraints

Permite-lhe escrever este código

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

O que é compilado

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

    public void MethodWithEnumConstraint<T>() where T: struct, Enum
    {
    }
}

Isso não responde a pergunta original, mas agora há um método .NET 4 chamado Enum.HasFlag que faz o que você está tentando fazer no seu exemplo

A maneira que eu faço é colocar uma estrutura de restrição e, em seguida, verifique se que T é um enum em tempo de execução.Isso não eliminar completamente o problema, mas não reduzi-lo um pouco

Como de C# 7.3, você pode usar o Enum restrição de tipos genéricos:

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

Se você quiser usar um Anulável enum, você deve deixar o orginial estrutura de restrição:

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

Utilizando seu código original, dentro do método você também pode usar a reflexão para que o teste T é um enum:

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

Aqui está o código que eu apenas fiz o que parece funcionar como você quer sem ter que fazer nada muito louco.Ele não é restrito a apenas enums definido como Bandeiras, mas não pode ser sempre uma seleção coloque em caso de necessidade.

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

se alguém precisa genérico IsSet (criado a partir da caixa de voar poderia ser melhorado), e ou string Enum onfly de conversão (que usa EnumConstraint apresentados abaixo):

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

    }

  }

Se alguém ainda precisa de exemplo quentes para criar o Enum codificação de restrição:

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

}

espero que isso ajude alguém.

Eu só queria acrescentar Enum como uma restrição genérica.

Enquanto isto é só para um pequeno método auxiliar utilizando ExtraConstraints é um pouco demais de sobrecarga para mim.

Eu decidi criar apenas um struct restrição e adicione uma verificação em tempo de execução para IsEnum.Para a conversão de uma variável de T para Enum eu o lancei para o objeto primeiro.

    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());
    }
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top