81

내가하고 싶은 일은 다음과 같습니다. 플래그가 지정된 값이 결합 된 enum이 있습니다.

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 #은 제약 조건이 enum 제한이없고 클래스와 구조체 만 포함하는 일반 클래스입니다. C #에서는 열거 형을 구조체로 보지 않습니다 (값 유형 임에도 불구하고). 이렇게 확장 유형을 추가 할 수 없습니다.

누구나 해결 방법을 알고 있습니까?


  • Keith : UnconstrainedMelody 버전 0.0.0.2 다운로드 - HasAll 및 HasAny를 구현했습니다. 즐겨. - Jon Skeet
  • "C #은 열거 형을 구조체로 보지 못합니다"라는 것은 무엇을 의미합니까? enum 유형을 유형 매개 변수로 사용할 수 있습니다.struct잘 됐네. - Timwi
  • 이 기사는 여기에서 확인하십시오 :codeproject.com/KB/cs/ExtendEnum.aspx' IsValidEnumValue ' ' IsFlagsEnumDefined ' 방법 아마도 귀하의 질문에 대한 답변입니다. - dmihailescu
  • 이 투표하십시오사용자의 아이디어, 언젠가 .net에 내장 된 것을보고 싶다면. - Matthieu
  • C #7.3열거 형 제약 조건을 도입합니다. - Marc Sigrist

12 답변


47

편집 : 현재 UnconstrainedMelody 버전 0.0.0.2에 살고 있습니다.

(요청한대로enum 제약에 대한 블로그 게시물. 독립 실행 형 답변을 위해 아래에 기본 사실을 포함했습니다.)

가장 좋은 해결책은 내가 그것을 기다릴 때까지 기다리는 것이다.제약되지 않은 멜로디1. 이것은 "가짜"제약 조건과 같은 C # 코드를 사용하는 라이브러리입니다.

where T : struct, IEnumConstraint

그것을

where T : struct, System.Enum

postbuild 단계를 통해.

쓰기가 너무 어렵지 않아야합니다.IsSet... 둘 다 취사하기는하지만Int64기반 및UInt64기반 플래그는 까다로운 부분이 될 수 있습니다. (나는 기본적인 헬퍼 메소드의 냄새를 맡는다. 기본적으로 모든 플래그 enum을 처리 할 수있게 해준다.UInt64.)

당신이 전화를 걸면 행동을 원합니까?

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

? 그걸 확인해야할까요?모든지정된 플래그가 설정되어 있습니까? 그것은 내 기대 일 것이다.

나는 오늘 집에 돌아가는 길에서 이것을하려고 노력할 것입니다 ... 유용한 enum 메소드에 대한 빠른 블리치를 사용하여 라이브러리를 사용 가능한 표준으로 신속하게 가져오고 조금만 쉬기를 바라고 있습니다.

편집 : 나는 잘 모르겠다.IsSet그 이름대로. 옵션 :

  • 포함
  • 내용
  • HasFlag (또는 HasFlags)
  • IsSet (확실히 옵션입니다)

환영합니다. 어쨌든 뭔가 돌이 세워지기까지는 오래있을 거라고 확신합니다.


1물론 패치로 제출하십시오.



16

대런, 형식이 특정 열거 형인 경우 작동합니다. 일반 열거 형을 사용하려면 부울 값 연산을 수행하기 위해 정수로 캐스팅해야합니다.

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


  • 그리고 만약 당신이 말도 안되는 수의 플래그를 가지고 있다면 GetTypeCode ()를 인자로, Convert.ToUint64 ()를 호출 할 수 있습니다. - Kit
  • 굉장합니다. '열거 형'과Convert.ToUInt32다른 곳을 찾지 못했습니다. AFAIK, 그것은 VB에서 작동하는 유일하게 점잖은 Pre-Net-4 솔루션입니다. BTW, ifmatchTo플래그 비트가 여러 개일 수 있습니다.!= 0== Convert.ToUInt32(matchTo). - ToolmakerSteve
  • 유의 사항Convert.ToUInt32열거 형과 함께 사용하면Convert.ToUInt32(object)오버로드입니다. CLR은 먼저이 값을 상자에 넣기 전에 상자에 넣습니다.ToUInt32방법. 대부분의 경우 문제가되지 않지만 초당 수백만 개의 열거 형을 파싱하는 데 이와 같은 방법을 사용하면 GC가 다소 혼란 스럽다는 것을 알고있는 것이 좋습니다. - Groo

9

실제로, 추악한 트릭으로 가능합니다. 그러나 확장 메소드에는 사용할 수 없습니다.

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>private 생성자와 public로 중첩 된 추상 상속 클래스Temp같이Enumenum이 아닌 버전의 상속 된 버전을 방지합니다.


8

일리노이 위빙 (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
    {
    }
}


8

C # 7.3부터 enum 제약 조건을 추가하는 기본 제공 방법이 있습니다.

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

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


  • 내 게시하기 전에 귀하의 답변을 보지 못했습니다. +1 - Mik
  • "허용 된" 가장 널리 보인 답변은 언어의 변화를 반영하기 위해 더 이상 최신이 아닙니다. 가장 대중적인 대답을 모르는 사람들은 가장 관련성이 높고 최신 정보를 얻지 못할 것입니다. - ForeverZer0
  • @ ForeverZer0 나는 대부분의 사람들이 그 대답을 사용하는 것이 오히려 늙었을 때 그 아래에 더 좋은 것이 존재한다는 것을 알고 있다고 생각합니다. - Mik
  • @Mik 분명히 큰 소리로 생각하고있었습니다. 그 점을 지적 해 주셔서 감사합니다. 현재 그것은 4 개의 답이 내려 있기 때문에 진술하고 있습니다. - ForeverZer0

4

이것은 원래의 질문에 대답하지 않지만 .NET 4에서 메소드가 호출되었습니다.Enum.HasFlag귀하의 예에서 당신이하려고하는 것을 수행합니다.


  • 이 시점에서 대부분의 사람들은 .NET 4 (이상)를 사용해야하므로 함께 해킹하려는 대신이 방법을 사용해야합니다. - CptRobby
  • Upvoted. 그러나 그들의 해결책은 논쟁의 권투를 사용합니다flag. .NET 4.0은 현재 5 년 전입니다. - Jeppe Stig Nielsen

3

내가하는 일은 struct 제약 조건을 넣은 다음 런타임에 T가 열거 형인지 확인합니다. 이것은 문제를 완전히 제거하지는 않지만 문제를 다소 줄입니다.


  • 여기서 T : struct, IComparable, IFormattable, IConvertible -이 열거 형에 가장 가까이 있습니다 :) - Kit

2

C # 7.3부터는 제네릭 형식에서 Enum 제약 조건을 사용할 수 있습니다.

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

Nullable enum을 사용하려면 orginial struct 제약 조건을 그대로 두어야합니다.

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


1

메소드 내에서 원래 코드를 사용하여 리플렉션을 사용하여 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;
    }
}


  • 고마워,하지만 그건 컴파일 타임 문제 (where 제약 조건)를 런타임 예외 (예외)로 바꾼다. 또한 무엇인가를하기 전에 입력을 int로 변환해야합니다. - Keith

1

제가 방금 한 코드 중 일부는 너무 미친 짓을하지 않고도 원하는 것처럼 작동하는 것 같습니다. Flags로 설정된 열거 형에 국한되지 않고 필요한 경우 항상 수표를 넣을 수 있습니다.

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


0

누군가가 일반 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&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));
  }

}

이 사람이 도움이되기를 바랍니다.


0

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

연결된 질문


관련된 질문

최근 질문