이 질문에는 이미 답변이 있습니다.
최신 정보: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같이Enumenum이 아닌 버전의 상속 된 버전을 방지합니다.
나는 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
{
// ...
}