열거형 일반 제약 조건이 없는 경우에 대한 좋은 해결 방법을 아는 사람이 있나요?

StackOverflow https://stackoverflow.com/questions/7244

  •  08-06-2019
  •  | 
  •  

문제

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

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#의 제네릭입니다.C#에서는 열거형이 값 유형임에도 불구하고 구조체로 표시되지 않으므로 이와 같은 확장 유형을 추가할 수 없습니다.

해결 방법을 아는 사람이 있나요?

도움이 되었습니까?

해결책

편집하다:이것은 이제 UnconstrainedMelody 버전 0.0.0.2에 적용되었습니다.

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

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

where T : struct, IEnumConstraint

그리고 그것을

where T : struct, System.Enum

빌드 후 단계를 통해.

글을 쓰는 것이 너무 어렵지는 않을 것이다. IsSet...비록 둘 다 음식을 제공하지만 Int64기반 및 UInt64기반 플래그는 까다로운 부분이 될 수 있습니다.(몇 가지 도우미 메서드 냄새가 나네요. 기본적으로 모든 플래그 열거형을 기본 유형이 있는 것처럼 처리할 수 있습니다. UInt64.)

전화를 걸면 어떤 행동을 하길 원하시나요?

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

?그걸 확인해야 하나? 모두 지정된 플래그가 설정되어 있습니까?그것이 나의 기대일 것이다.

오늘 밤 집에 가는 길에 해봐야겠네요...나는 라이브러리를 사용 가능한 표준에 신속하게 맞추고 조금 긴장을 풀 수 있도록 유용한 열거형 메서드에 대한 빠른 공세를 펼치기를 바라고 있습니다.

편집하다:잘 모르겠습니다 IsSet 그건 그렇고, 이름으로.옵션:

  • 포함
  • 포함
  • HasFlag(또는 HasFlags)
  • IsSet(확실히 옵션임)

생각을 환영합니다.어쨌든 뭔가가 확정되기까지는 시간이 좀 걸릴 거라고 확신합니다...


1 아니면 패치로 제출하세요...

다른 팁

대런, 유형이 특정 열거형이면 작동할 것입니다. 일반 열거형이 작동하려면 부울 수학을 수행하기 위해 정수(또는 더 가능성이 높은 단위)로 캐스팅해야 합니다.

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

C# 7.3부터 열거형 제약 조건을 추가하는 방법이 기본 제공됩니다.

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

원천: https://docs.microsoft.com/en-us/dotnet/csharp/언어-reference/keywords/where-generic-type-constraint

사실, 추악한 트릭을 사용하면 가능합니다.그러나 확장 방법에는 사용할 수 없습니다.

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 ~처럼 Enum, 비열거형에 대한 상속된 버전을 방지합니다.

IL Weaving을 사용하여 이를 달성할 수 있으며 추가 제약

이 코드를 작성할 수 있습니다

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

이것은 원래 질문에 대한 대답은 아니지만 이제 .NET 4에는 다음과 같은 메서드가 있습니다. Enum.HasFlag 귀하의 예에서 당신이하려는 일을 수행합니다

내가 하는 방법은 구조체 제약 조건을 넣은 다음 런타임에 T가 열거형인지 확인하는 것입니다.이렇게 하면 문제가 완전히 제거되지는 않지만 어느 정도 줄어들 수 있습니다.

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

원래 코드를 사용하면 메서드 내에서 리플렉션을 사용하여 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으로 즉시 변환(아래에 제시된 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));
  }

}

이것이 누군가에게 도움이 되기를 바랍니다.

저는 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());
    }
라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top