10

최신 정보: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포스트 빌드. 이것이 바로이 시점에서 가장 가까운 사람들이이 문제를 해결할 수 있다는 것입니다.

만나다:

이 해결 방법을 실행할 수없는 경우 라이브러리를 C ++ / CLI 코드로 작성해야합니다.이 코드는 제네릭 형식 제약 조건에 사용할 수있는 것을 제한하지 않습니다 (아래의 내 대답의 코드 참조).


  • 드문 경우이지만 이러한 제약이 필요하다고 느끼지 않았을 수 있습니다. 누구가 알고 있는지 ... 나는 그 (것)들 경우에 그들과 동의 할 것입니다 :) - Skurmedel
  • @Skurmedel : 이상한 점은 구체적으로 금지되어 있다는 것입니다. 즉, 스펙이 더 많이 만들어졌습니다.복잡한그것을 제한하십시오. 이유를 알 수 없습니다. 어쩌면 에릭 리 퍼트가 설명 할거야 :) - Jon Skeet
  • 그렇습니다. Jon Skeet에 동의합니다. 간과 할 수는 있습니다. (클래스 상속과 같은 제약 조건에 대해 동일한 상속 확인을 사용했습니다 ...)하지만 C #의 내부 동작에 대해서는 잘 모르겠습니다. . - Blixt
  • Eric Lippert는 이전에 C #에서 Enum Type Constraints에 대해 게시했습니다.stackoverflow.com/questions/1331739/enum-type-constraints-in-c/… - Randy Levy
  • 내가 참조. 나는 컴파일러 오류를 끌 수있는 옵션이 있어도 코드가 여전히 작동하지 않는다고 대답했다. 그런 경우가 아니라면 왜 이런 제약이 내게 강요되는지 알 수 없습니다 (CS0702 오류를 명시 적으로 없애는 것이 좋을 것입니다). - Blixt

5 답변


10

편집 : ildasm / ilasm을 통해 이것을 지원하는 라이브러리를 지금 사용할 수 있습니다.제약되지 않은 멜로디.


C # 팀 구성원은 이전에처럼지원할 수있다.where T : Enumwhere 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
  • @Blixt : 글쎄, 바이너리 다시 쓰기를 들여다 볼 수도 있지만 아마도 추악 할 것이다. 나는 델리 케이트와 enums로 할 수있는 "유용한 일들"을위한 오픈 소스 프로젝트에 관심이 있는지 궁금하다. - Jon Skeet
  • 도서관 아이디어 주위의 블로그 게시물 ...msmvps.com/blogs/jon_skeet/archive/2009/09/10/… - Jon Skeet
  • ILMerge를 사용할 수 있습니다 (research.microsoft.com/en-us/people/mbarnett/ilmerge.aspx)를 사용하여 주 코드에 C ++ / CLI 어셈블리를 포함시킬 수 있습니다. - Steve Gilham
  • @ Yngvar : 이제 codeblog.jonskeet.uk이되었습니다.codeblog.jonskeet.uk/2009/09/10/… - Jon Skeet

7

그 해결 방법이 있습니다.

여길 봐


  • 해결 방법이 작동하는 동안 & quot; C # 3.0에서는 & quot;이 아닙니다. - Lasse Vågsæther Karlsen
  • 흠 ... 확장 방법이 하나뿐이기 때문에 새로운 C ++ 프로젝트를 만들고 싶지 않습니다. 나는 그것이 C ++ / CLI를 C # 라이브러리로 컴파일하기위한 환경을 설정하는 것이 가능한가 의심 스럽다. - Blixt
  • 그것은 좋은 해결 방법입니다. 나는 이것이 왜 4 년이 지나면 알려지지 않았는지 궁금해합니다 ... - Daniel Brückner
  • 해결 방법을 구현했으며 완벽하게 작동합니다! 슬프게도 나는 전체 유틸리티 라이브러리를 C ++ 코드로 만들지 않는 한 자신을 사용하지 않는다. 어쨌든 코드에 대한이 질문에 대한 나의 대답을 보라. - Blixt

3

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

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이 아닌 버전의 상속 된 버전을 방지합니다.



2

나는 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);
        }
    }
}


1

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

연결된 질문


관련된 질문

최근 질문