내가하고 싶은 일은 다음과 같습니다. 플래그가 지정된 값이 결합 된 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 #에서는 열거 형을 구조체로 보지 않습니다 (값 유형 임에도 불구하고). 이렇게 확장 유형을 추가 할 수 없습니다.
누구나 해결 방법을 알고 있습니까?
편집 : 현재 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
그 이름대로. 옵션 :
환영합니다. 어쨌든 뭔가 돌이 세워지기까지는 오래있을 거라고 확신합니다.
1물론 패치로 제출하십시오.
colors.HasAny(Colors.Red | Colors.Blue)
아주 읽을 수있는 코드처럼 보입니다.=)
- Blixt
대런, 형식이 특정 열거 형인 경우 작동합니다. 일반 열거 형을 사용하려면 부울 값 연산을 수행하기 위해 정수로 캐스팅해야합니다.
public static bool IsSet( this Enum input, Enum matchTo )
{
return ( Convert.ToUInt32( input ) & Convert.ToUInt32( matchTo ) ) != 0;
}
Convert.ToUInt32
다른 곳을 찾지 못했습니다. AFAIK, 그것은 VB에서 작동하는 유일하게 점잖은 Pre-Net-4 솔루션입니다. BTW, ifmatchTo
플래그 비트가 여러 개일 수 있습니다.!= 0
와== Convert.ToUInt32(matchTo)
. - ToolmakerSteveConvert.ToUInt32
열거 형과 함께 사용하면Convert.ToUInt32(object)
오버로드입니다. CLR은 먼저이 값을 상자에 넣기 전에 상자에 넣습니다.ToUInt32
방법. 대부분의 경우 문제가되지 않지만 초당 수백만 개의 열거 형을 파싱하는 데 이와 같은 방법을 사용하면 GC가 다소 혼란 스럽다는 것을 알고있는 것이 좋습니다. - Groo
실제로, 추악한 트릭으로 가능합니다. 그러나 확장 메소드에는 사용할 수 없습니다.
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
같이Enum
enum이 아닌 버전의 상속 된 버전을 방지합니다.
일리노이 위빙 (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
{
}
}
C # 7.3부터 enum 제약 조건을 추가하는 기본 제공 방법이 있습니다.
public class UsingEnum<T> where T : System.Enum { }
이것은 원래의 질문에 대답하지 않지만 .NET 4에서 메소드가 호출되었습니다.Enum.HasFlag귀하의 예에서 당신이하려고하는 것을 수행합니다.
flag
. .NET 4.0은 현재 5 년 전입니다. - Jeppe Stig Nielsen
내가하는 일은 struct 제약 조건을 넣은 다음 런타임에 T가 열거 형인지 확인합니다. 이것은 문제를 완전히 제거하지는 않지만 문제를 다소 줄입니다.
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;
}
메소드 내에서 원래 코드를 사용하여 리플렉션을 사용하여 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;
}
}
제가 방금 한 코드 중 일부는 너무 미친 짓을하지 않고도 원하는 것처럼 작동하는 것 같습니다. 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.");
}
}
누군가가 일반 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<Enum>Parse<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());
}
struct
잘 됐네. - Timwi