이 질문에는 이미 답변이 있습니다.
최신 정보:C #해결 방법은이 질문의 맨 아래 부분을 참조하십시오.
안녕,
다음 확장 방법을 고려하십시오.
public static bool HasFlags<T>(this T value, T flags)
where T : System.Enum
{
// ...
}
컴파일시에 클래스를 상속 할 수 없으므로, 알 수 있듯이 컴파일 타임에 오류가 발생합니다.System.Enum
. 문제는 지정된 열거 형이enum
키워드는 실제로 상속합니다.System.Enum
, 위의 코드는 확장 메서드를 열거 형으로 제한하는 이상적인 방법입니다.
이제 여기서 명백한 해결 방법은Enum
대신에T
, 그런 다음 일반적인 유형의 이점을 잃게됩니다.
MyEnum e;
e.HasFlags(MyOtherEnum.DoFunkyStuff);
위의 코드는 제네릭 형식을 사용하여 컴파일 타임 오류를 발생시키는 반면 런타임 오류는Enum
형식 (만약 내가 이렇게 구현합니다.)
제약 조건 검사를 끄는 데 사용할 수있는 컴파일러 옵션이 있습니까? 아니면이를 수행하는 다른 멋진 방법이 있습니까?
제안되기 전에, 나는 사용하지 않을 것이라고 말하고 싶다.where T : struct
또는 그와 같은 것, 그때 이후로 당신은 이상한 것을 할 수있을 것입니다.123.HasFlags(456)
.
나는이 오류가 전혀 존재하지 않는 이유에 대해 왜곡되어있다 ... 당신이 사용하는 것과 같은 문제이다.where T : System.Object
, 그러나 당신은 그것을 가지고 있습니다.where T : class
... 왜 안돼?where T : enum
?
C #해결 방법
Jon Skeet은 제약 조건이있는 클래스를 컴파일하는 라이브러리에 대한 작업을 시작했습니다.
IEnumConstraint
, 다음으로 대체됩니다System.Enum
포스트 빌드. 이것이 바로이 시점에서 가장 가까운 사람들이이 문제를 해결할 수 있다는 것입니다.만나다:
- 코드 프로젝트 :http://code.google.com/p/unconstrained-melody/
- 블로그 항목:http://msmvps.com/blogs/jon_skeet/archive/2009/09/10/generic-constraints-for-enums-and-delegates.aspx
이 해결 방법을 실행할 수없는 경우 라이브러리를 C ++ / CLI 코드로 작성해야합니다.이 코드는 제네릭 형식 제약 조건에 사용할 수있는 것을 제한하지 않습니다 (아래의 내 대답의 코드 참조).
편집 : ildasm / ilasm을 통해 이것을 지원하는 라이브러리를 지금 사용할 수 있습니다.제약되지 않은 멜로디.
C # 팀 구성원은 이전에처럼지원할 수있다.where T : Enum
과where T : Delegate
, 그러나 그것은 결코 충분히 높은 우선 순위가 아니었다. (나는 추론이 처음에는 제한을 가졌다는 것에 대해 확신 할 수 없다.
C #에서 가장 실용적인 해결 방법은 다음과 같습니다.
public static bool HasFlags<T>(this T value, T flags) where T : struct
{
if (!(value is Enum))
{
throw new ArgumentException();
}
// ...
}
컴파일 타임에 "enum-ness"를 검사하지 않지만 두 유형 모두에서 동일한 유형을 사용하고 있는지 확인합니다. 물론 체크의 실행 시간 패널티도 있습니다. 정적 생성자에서 예외를 throw하는 구현에 대해 일반적인 중첩 형식을 사용하여 첫 번째 호출 후에 실행 시간 패널티를 피할 수 있습니다.
public static bool HasFlags<T>(this T value, T flags) where T : struct
{
if (!(value is Enum))
{
throw new ArgumentException();
}
return EnumHelper<T>.HasFlags(value, flags);
}
private class EnumHelper<T> where T : struct
{
static EnumHelper()
{
if (!typeof(Enum).IsAssignableFrom(typeof(T))
{
throw new InvalidOperationException(); // Or something similar
}
}
internal static HasFlags(T value, T flags)
{
...
}
}
Greco가 언급했듯이 C ++ / CLI로이 메서드를 작성한 다음 C #의 클래스 라이브러리를 다른 옵션으로 참조 할 수 있습니다.
where T : struct
내 질문에 언급 된 이유로 인해 (123.HasFlags(456)
Greco의 솔루션이 마음에 들지만 한 가지 방법으로 전체 라이브러리를 만드는 것을 정당화 할 수는 없습니다. 프로세스를 "주입"할 수 있습니까? 컴파일 할 때 C ++ 라이브러리에 C ++ / CLI? - Blixt
그 해결 방법이 있습니다.
실제로, 추악한 트릭으로 가능합니다. 그러나 확장 메소드에는 사용할 수 없습니다.
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.Parse<DateTimeKind>("Local")
원한다면Enums<Temp>
private 생성자와 public로 중첩 된 추상 상속 클래스Temp
같이Enum
enum이 아닌 버전의 상속 된 버전을 방지합니다.
나는 C ++로 돌아가는 것에 저항 할 수 없었고, 작업을 마친 이후로 나머지 부분과 공유 할 것이라고 생각했습니다!
여기에 C ++ 코드가 있습니다. (C ++은 매우 녹슬 었으므로 에러를 지적하십시오. 특히 인수가 어떻게 정의되는지 알려주십시오) :
#include "stdafx.h"
using namespace System;
using namespace System::Runtime::CompilerServices;
namespace Blixt
{
namespace Utilities
{
[Extension]
public ref class EnumUtility abstract sealed
{
public:
generic <typename T> where T : value class, Enum
[Extension]
static bool HasFlags(T value, T flags)
{
__int64 mask = Convert::ToInt64(flags);
return (Convert::ToInt64(value) & mask) == mask;
}
};
}
}
그리고 테스트 용 C # 코드 (콘솔 응용 프로그램) :
using System;
using Blixt.Utilities;
namespace Blixt.Playground
{
[Flags]
public enum Colors : byte
{
Black = 0,
Red = 1,
Green = 2,
Blue = 4
}
[Flags]
public enum Tastes : byte
{
Nothing = 0,
Sour = 1,
Sweet = 2,
Bitter = 4,
Salty = 8
}
class Program
{
static void Main(string[] args)
{
Colors c = Colors.Blue | Colors.Red;
Console.WriteLine("Green and blue? {0}", c.HasFlags(Colors.Green | Colors.Red));
Console.WriteLine("Blue? {0}", c.HasFlags(Colors.Blue));
Console.WriteLine("Green? {0}", c.HasFlags(Colors.Green));
Console.WriteLine("Red and blue? {0}", c.HasFlags(Colors.Red | Colors.Blue));
// Compilation error:
//Console.WriteLine("Sour? {0}", c.HasFlags(Tastes.Sour));
Console.WriteLine("Press any key to exit...");
Console.ReadKey(true);
}
}
}
일리노이 위빙 (IL Weaving)과ExtraConstraints
이 코드를 작성할 수 있습니다.
public static bool HasFlags<[EnumConstraint] T>(this T value, T flags)
{
// ...
}
무엇이 컴파일 되나요?
public static bool HasFlags<T>(this T value, T flags)
where T : System.Enum
{
// ...
}