열거 형의 문자열 표현


912

다음과 같은 열거 형이 있습니다.

public enum AuthenticationMethod
{
    FORMS = 1,
    WINDOWSAUTHENTICATION = 2,
    SINGLESIGNON = 3
}

그러나 문제는 ID 1이 아닌 AuthenticationMethod.FORMS를 요청할 때 "FORMS"라는 단어가 필요하다는 것입니다.

이 문제에 대한 다음 해결책을 찾았습니다 ( link ).

먼저 "StringValue"라는 사용자 정의 속성을 작성해야합니다.

public class StringValue : System.Attribute
{
    private readonly string _value;

    public StringValue(string value)
    {
        _value = value;
    }

    public string Value
    {
        get { return _value; }
    }

}

그런 다음이 속성을 열거 자에 추가 할 수 있습니다.

public enum AuthenticationMethod
{
    [StringValue("FORMS")]
    FORMS = 1,
    [StringValue("WINDOWS")]
    WINDOWSAUTHENTICATION = 2,
    [StringValue("SSO")]
    SINGLESIGNON = 3
}

그리고 물론 그 StringValue를 검색 할 것이 필요합니다.

public static class StringEnum
{
    public static string GetStringValue(Enum value)
    {
        string output = null;
        Type type = value.GetType();

        //Check first in our cached results...

        //Look for our 'StringValueAttribute' 

        //in the field's custom attributes

        FieldInfo fi = type.GetField(value.ToString());
        StringValue[] attrs =
           fi.GetCustomAttributes(typeof(StringValue),
                                   false) as StringValue[];
        if (attrs.Length > 0)
        {
            output = attrs[0].Value;
        }

        return output;
    }
}

이제 열거 자에 대한 문자열 값을 가져 오는 도구가 있습니다. 그런 다음 다음과 같이 사용할 수 있습니다.

string valueOfAuthenticationMethod = StringEnum.GetStringValue(AuthenticationMethod.FORMS);

자 이제이 모든 것들이 매력처럼 보이지만 나는 많은 일을합니다. 더 나은 해결책이 있는지 궁금합니다.

나는 또한 사전 및 정적 속성으로 무언가를 시도했지만 그다지 좋지 않았습니다.


8
좋은! 이것을 사용하여 열거 형 값을 현지화 된 문자열로 변환 할 수 있습니다.
Øyvind Skaar

5
이 긴 바람을 찾을 수 있지만 실제로 다른 것들을 갈 수있는 매우 유연한 방법입니다. 내 동료 중 한 사람이 지적했듯이, 데이터베이스 코드를 열거 형 값 등으로 매핑하는 열거 형 도우미를 대체하는 데 많은 경우에 사용될 수 있습니다.
BenAlabaster

27
MSDN은 "Attribute"접미사로 접미사 특성 클래스를 철회합니다. 따라서 "class StringValueAttribute";)
serhio

14
@BenAlabaster에 동의합니다. 이것은 실제로 매우 유연합니다. 또한 정적 메소드 this에서 앞에 추가하여 확장 메소드로 만들 수도 Enum있습니다. 그렇다면 당신은 할 수 있습니다 AuthenticationMethod.Forms.GetStringValue();
저스틴 피 호니

5
이 접근법은 리플렉션을 사용하여 속성 값을 읽으며 내 경험에서 GetStringValue ()를 여러 번 호출 해야하는 경우 매우 느립니다. 형식 안전 열거 형 패턴이 더 빠릅니다.
Rn222

답변:


868

형식 안전 열거 형 패턴을 사용해보십시오 .

public sealed class AuthenticationMethod {

    private readonly String name;
    private readonly int value;

    public static readonly AuthenticationMethod FORMS = new AuthenticationMethod (1, "FORMS");
    public static readonly AuthenticationMethod WINDOWSAUTHENTICATION = new AuthenticationMethod (2, "WINDOWS");
    public static readonly AuthenticationMethod SINGLESIGNON = new AuthenticationMethod (3, "SSN");        

    private AuthenticationMethod(int value, String name){
        this.name = name;
        this.value = value;
    }

    public override String ToString(){
        return name;
    }

}

업데이트 명시 적 (또는 암시 적) 유형 변환은 다음을 통해 수행 할 수 있습니다.

  • 매핑으로 정적 필드 추가

    private static readonly Dictionary<string, AuthenticationMethod> instance = new Dictionary<string,AuthenticationMethod>();
    • nb 인스턴스 생성자를 호출 할 때 "enum member"필드의 초기화로 NullReferenceException이 발생하지 않도록하려면 클래스의 "enum member"필드 앞에 Dictionary 필드를 배치해야합니다. 이것은 정적 필드 이니셜 라이저가 선언 순서대로, 그리고 정적 생성자 전에 호출되기 때문에 모든 정적 필드가 초기화되기 전에 그리고 정적 생성자가 호출되기 전에 인스턴스 생성자가 호출 될 수있는 이상하고 필요하지만 혼란스러운 상황이 발생하기 때문입니다.
  • 인스턴스 생성자에서이 매핑을 작성

    instance[name] = this;
  • 및 추가 사용자 정의 형식 변환 연산자

    public static explicit operator AuthenticationMethod(string str)
    {
        AuthenticationMethod result;
        if (instance.TryGetValue(str, out result))
            return result;
        else
            throw new InvalidCastException();
    }

17
열거 형처럼 보이지만 열거 형이 아닙니다. 사람들이 AuthenticationMethods를 비교하려고 시도하면 흥미로운 문제가 발생한다고 상상할 수 있습니다. 다양한 등식 연산자에도 과부하가 필요할 수 있습니다.
Ant 님

36
@Ant : 필요가 없습니다. 각 AuthenticationMethod의 인스턴스가 하나만 있으므로 Object에서 상속 된 참조 동등성이 올바르게 작동합니다.
Jakub Šturc

10
@tyriker : 컴파일러가합니다. 생성자는 비공개이므로 새 인스턴스를 만들 수 없습니다. 또한 정적 멤버는 인스턴스를 통해 액세스 할 수 없습니다.
Jakub Šturc

21
@Jakub 매우 흥미 롭습니다. 나는 그것을 사용하는 방법을 알아 내고 그 이점을 깨닫기 위해 그것을 가지고 놀아야했습니다. 정적이 아닌 공개 클래스이지만 인스턴스화 할 수 없으며 정적 멤버에만 액세스 할 수 있습니다. 기본적으로 열거 형처럼 동작합니다. 그러나 가장 중요한 부분은 정적 멤버가 클래스의 유형이며 일반적인 문자열이나 int가 아니라는 것입니다. ... [기다려요] ... 유형 안전 열거 형입니다! 이해하도록 도와 주셔서 감사합니다.
tyriker

6
@kiran 아래에서 Switch-Case 문과 함께 사용할 수 있도록 약간 수정 된 Jakub Šturc의 답변을 게시 했으므로이 접근법에 대한 단점은 없습니다 :)
deadlydog

228

사용 방법

Enum.GetName(Type MyEnumType,  object enumvariable)  

에서와 같이 ( Shipper정의 된 열거 형 이라고 가정 )

Shipper x = Shipper.FederalExpress;
string s = Enum.GetName(typeof(Shipper), x);

Enum 클래스에는 조사 할 가치가있는 다른 정적 메소드가 많이 있습니다 ...


5
바로 그거죠. 문자열 설명에 대한 사용자 지정 특성을 만들었지 만 ComboBox 등에 쉽게 바인딩 할 수있는 사용자 친화적 인 버전 (공백 및 기타 특수 문자 포함)을 원하기 때문입니다.
lc.

5
Enum.GetName은 .ToString ()과 동일한 열거 형의 필드 이름을 반영합니다. 성능이 문제인 경우 문제가 될 수 있습니다. 비록 많은 열거 형을 변환하지 않으면 걱정하지 않아도됩니다.
Keith

8
고려해야 할 또 다른 옵션은 추가 기능이있는 열거 형이 필요한 경우 구조체를 사용하여 "yr 자신의 롤"하는 것입니다 ... 구조체의 개별 인스턴스를 생성하는 생성자에 초기화 된 열거 형 값을 나타내는 정적 읽기 전용 명명 된 속성을 추가합니다 ...
Charles Bretana

1
그런 다음 원하는 "구조"멤버가 원하는 기능을 구현하기 위해 원하는 다른 구조체 멤버를 추가 할 수 있습니다.
Charles Bretana

2
여기서 문제는 GetName을 지역화 할 수 없다는 것입니다. 항상 걱정할 필요는 없지만 알고 있어야합니다.
Joel Coehoorn

79

ToString ()을 사용하여 값이 아닌 이름을 참조 할 수 있습니다.

Console.WriteLine("Auth method: {0}", AuthenticationMethod.Forms.ToString());

설명서는 다음과 같습니다.

http://msdn.microsoft.com/en-us/library/16c1xs4z.aspx

... 파스칼 케이스에서 열거 형의 이름을 지정하면 (IsMyMynumValue = 1과 같은) 매우 간단한 정규식을 사용하여 친숙한 양식을 인쇄 할 수 있습니다.

static string ToFriendlyCase(this string EnumString)
{
    return Regex.Replace(EnumString, "(?!^)([A-Z])", " $1");
}

어느 문자열에서나 쉽게 호출 할 수 있습니다.

Console.WriteLine("ConvertMyCrazyPascalCaseSentenceToFriendlyCase".ToFriendlyCase());

출력 :

My Crazy Pascal Case Sentence를 Friendly Case로 변환

따라서 사용자 정의 속성을 작성하고 열거 형에 첨부하거나 룩업 테이블을 사용하여 친숙한 문자열과 열거 형 값을 결합하고 자체 관리하는 것이 가장 좋으며 무한히 파스칼 케이스 문자열에서 사용할 수 있습니다 더 재사용 가능합니다. 물론 솔루션에서 제공하는 열거 형 과 다른 이름 을 사용할 수는 없습니다 .

그래도 더 복잡한 시나리오의 경우 원래 솔루션을 좋아합니다. 솔루션을 한 단계 더 발전시키고 GetStringValue를 열거 형의 확장 메소드로 만들면 StringEnum.GetStringValue와 같이 참조 할 필요가 없습니다 ...

public static string GetStringValue(this AuthenticationMethod value)
{
  string output = null;
  Type type = value.GetType();
  FieldInfo fi = type.GetField(value.ToString());
  StringValue[] attrs = fi.GetCustomAttributes(typeof(StringValue), false) as StringValue[];
  if (attrs.Length > 0)
    output = attrs[0].Value;
  return output;
}

그런 다음 열거 형 인스턴스에서 바로 액세스 할 수 있습니다.

Console.WriteLine(AuthenticationMethod.SSO.GetStringValue());

2
"친숙한 이름"에 공백이 필요한 경우에는 도움이되지 않습니다. "양식 인증"과 같은
Ray Booysen

4
따라서 열거 형에 FormsAuthentication과 같은 대문자로 이름을 지정하고 시작 부분이 아닌 대문자 앞에 공백을 삽입하십시오. 그것은 ... 문자열에 공백을 삽입하기 위해 로켓 과학이 아니다
BenAlabaster

4
Pascal Case 이름의 자동 간격은 대문자 또는 대문자와 같은 약어를 포함하는 경우 문제가됩니다 (예 : XML 또는 GPS).
Richard Ev

2
@RichardEv에는 완벽한 정규 표현식이 없지만 약어로 조금 더 잘 작동해야합니다. "(?!^)([^A-Z])([A-Z])", "$1 $2". 이렇게 HereIsATEST됩니다 Here Is ATEST.
스페어 바이트

이 작은 "해킹"을 수행하는 것은 비현실적이지 않습니다. OP가 말한 것을 얻었고 비슷한 솔루션을 찾으려고합니다. 즉 Enum의 우아함을 사용하지만 관련 메시지에 쉽게 액세스 할 수 있습니다. 내가 생각할 수있는 유일한 해결책은 열거 형 이름과 문자열 값 사이에 일종의 매핑을 적용하는 것이지만 문자열 데이터를 유지하는 문제를 해결할 수는 없습니다 (그러나 다중 지역 등이 필요한 시나리오에는 실용적입니다) )
Tahir Khalid

72

불행히도 열거 형에 속성을 가져 오는 것은 매우 느립니다.

이 질문을보십시오 : 누구나 enum 값에 대한 사용자 정의 속성을 얻는 빠른 방법을 알고 있습니까?

.ToString()너무 열거에 매우 느립니다.

그래도 열거 형의 확장 메소드를 작성할 수 있습니다.

public static string GetName( this MyEnum input ) {
    switch ( input ) {
        case MyEnum.WINDOWSAUTHENTICATION:
            return "Windows";
        //and so on
    }
}

이것은 훌륭하지는 않지만 속성이나 필드 이름에 대한 반영이 필요하지 않습니다.


C # 6 업데이트

당신은 C # 6을 사용할 수있는 경우 새로운 nameof운영자는 그래서, 열거 작동 nameof(MyEnum.WINDOWSAUTHENTICATION)으로 변환됩니다 "WINDOWSAUTHENTICATION"컴파일 시간 이 열거 이름을 얻을 수있는 가장 빠른 방법 만들기.

이렇게하면 명시 적 열거 형을 인라인 상수로 변환하므로 변수에있는 열거 형에서는 작동하지 않습니다. 그래서:

nameof(AuthenticationMethod.FORMS) == "FORMS"

그러나...

var myMethod = AuthenticationMethod.FORMS;
nameof(myMethod) == "myMethod"

24
속성 값을 한 번 가져 와서 Dictionary <MyEnum, string>에 넣어 선언적 측면을 유지할 수 있습니다.
Jon Skeet

1
예, 이것이 우리가 반사가 병목 현상이라는 것을 알았을 때 많은 열거 형이있는 응용 프로그램에서 수행 한 결과입니다.
Keith

Jon과 Keith에게 감사합니다. 나는 사전 제안을 사용했습니다. 훌륭하고 빠릅니다!
Helge Klein

@ JonSkeet 나는 이것이 오래된 것을 알고있다. 그러나 어떻게 이것을 달성 할 수 있습니까?
user919426

2
@ user919426 : 원하십니까? 그것들을 사전에 넣습니까? 컬렉션 이니셜 라이저를 사용하여 사전을 만드는 것만으로는 원하는 것을 명확하게 알 수 없습니다.
Jon Skeet

59

확장 방법을 사용합니다.

public static class AttributesHelperExtension
    {
        public static string ToDescription(this Enum value)
        {
            var da = (DescriptionAttribute[])(value.GetType().GetField(value.ToString())).GetCustomAttributes(typeof(DescriptionAttribute), false);
            return da.Length > 0 ? da[0].Description : value.ToString();
        }
}

이제로 장식하십시오 enum:

public enum AuthenticationMethod
{
    [Description("FORMS")]
    FORMS = 1,
    [Description("WINDOWSAUTHENTICATION")]
    WINDOWSAUTHENTICATION = 2,
    [Description("SINGLESIGNON ")]
    SINGLESIGNON = 3
}

전화 할 때

AuthenticationMethod.FORMS.ToDescription()당신은 얻을 것이다 "FORMS".


1
using System.ComponentModel;또한 추가해야했습니다 .이 방법은 String 값을 Enum의 이름과 동일하게하려는 경우에만 작동합니다. OP는 다른 가치를 원했습니다.
elcool

2
전화 할 때 당신은 의미하지 AuthenticationMethod.FORMS.ToDescription()않습니까?
nicodemus13

41

그냥 ToString()방법을 사용하십시오

public enum any{Tomato=0,Melon,Watermelon}

문자열을 참조하려면 다음을 Tomato사용하십시오.

any.Tomato.ToString();

와. 그것은 쉽다. OP가 사용자 정의 문자열 설명을 추가하기를 원했지만 이것이 필요했습니다. 회고 적으로 이것을 시도하는 것으로 알고 있었지만 Enum.GetName 경로를 내려갔습니다.
Rafe

7
왜 다른 사람들이 이것을 지나치게 복잡하게 만드는가?
Brent

18
@ 브렌트 대부분의 경우 .ToString()필요한 값과 사용자 친화적 인 값이 다릅니다.
Novitchi S

2
@ 브렌트-이것은 묻는 질문과 다르기 때문에. 질문은 열거 된 값이 할당 된 변수 에서이 문자열을 얻는 방법입니다. 그것은 런타임에 역동적입니다. 유형 정의를 확인하고 런타임에 설정합니다.
Hogan

1
@Hogan-ToString ()은 변수에서도 작동합니다. any fruit = any.Tomato; string tomato = fruit.ToString();
LiborV

29

.Net 4.0 이상에서 매우 간단한 해결책입니다. 다른 코드는 필요하지 않습니다.

public enum MyStatus
{
    Active = 1,
    Archived = 2
}

문자열을 얻으려면 다음을 사용하십시오.

MyStatus.Active.ToString("f");

또는

MyStatus.Archived.ToString("f");`

값은 "Active"또는 "Archived"입니다.

호출 할 때 다른 문자열 형식 (위의 "f")을 Enum.ToString보려면이 열거 형식 문자열 페이지를 참조하십시오.


28

System.ComponentModel 네임 스페이스의 Description 특성을 사용합니다. 열거 형을 장식 한 다음이 코드를 사용하여 검색하십시오.

public static string GetDescription<T>(this object enumerationValue)
            where T : struct
        {
            Type type = enumerationValue.GetType();
            if (!type.IsEnum)
            {
                throw new ArgumentException("EnumerationValue must be of Enum type", "enumerationValue");
            }

            //Tries to find a DescriptionAttribute for a potential friendly name
            //for the enum
            MemberInfo[] memberInfo = type.GetMember(enumerationValue.ToString());
            if (memberInfo != null && memberInfo.Length > 0)
            {
                object[] attrs = memberInfo[0].GetCustomAttributes(typeof(DescriptionAttribute), false);

                if (attrs != null && attrs.Length > 0)
                {
                    //Pull out the description value
                    return ((DescriptionAttribute)attrs[0]).Description;
                }
            }
            //If we have no description attribute, just return the ToString of the enum
            return enumerationValue.ToString();

        }

예로서:

public enum Cycle : int
{        
   [Description("Daily Cycle")]
   Daily = 1,
   Weekly,
   Monthly
}

이 코드는 "친숙한 이름"이 필요없고 열거 형의 .ToString () 만 반환하는 열거 형에 적합합니다.


27

나는 Jakub Šturc의 대답을 정말로 좋아하지만 단점은 switch-case 문으로 사용할 수 없다는 것입니다. 다음은 switch 문과 함께 사용할 수있는 약간 수정 된 버전의 답변입니다.

public sealed class AuthenticationMethod
{
    #region This code never needs to change.
    private readonly string _name;
    public readonly Values Value;

    private AuthenticationMethod(Values value, String name){
        this._name = name;
        this.Value = value;
    }

    public override String ToString(){
        return _name;
    }
    #endregion

    public enum Values
    {
        Forms = 1,
        Windows = 2,
        SSN = 3
    }

    public static readonly AuthenticationMethod FORMS = new AuthenticationMethod (Values.Forms, "FORMS");
    public static readonly AuthenticationMethod WINDOWSAUTHENTICATION = new AuthenticationMethod (Values.Windows, "WINDOWS");
    public static readonly AuthenticationMethod SINGLESIGNON = new AuthenticationMethod (Values.SSN, "SSN");
}

따라서 Jakub Šturc의 답변의 모든 이점을 얻을 수 있으며 다음과 같이 switch 문과 함께 사용할 수 있습니다.

var authenticationMethodVariable = AuthenticationMethod.FORMS;  // Set the "enum" value we want to use.
var methodName = authenticationMethodVariable.ToString();       // Get the user-friendly "name" of the "enum" value.

// Perform logic based on which "enum" value was chosen.
switch (authenticationMethodVariable.Value)
{
    case authenticationMethodVariable.Values.Forms: // Do something
        break;
    case authenticationMethodVariable.Values.Windows: // Do something
        break;
    case authenticationMethodVariable.Values.SSN: // Do something
        break;      
}

더 짧은 해결책은 열거 형 {}을 제거하고 대신 구성된 Enum 수를 정적으로 계산하는 것입니다. 또한 열거 형 목록에 새 인스턴스를 추가 할 필요가 없다는 이점도 있습니다. 예 public static int nextAvailable { get; private set; }를 들어 생성자에서this.Value = nextAvailable++;
kjhf

재미있는 아이디어 @kjhf. 누군가가 코드를 재정렬하면 열거 형 값에 할당 된 값도 변경 될 수 있습니다. 예를 들어, 열거 형 값이 파일 / 데이터베이스에 저장되고 "새 AuthenticationMethod (...)"행의 순서가 변경된 경우 (예 : 하나가 제거됨) 잘못된 열거 형 값이 검색 될 수 있습니다. 앱을 다시 실행하고 파일 / 데이터베이스에서 열거 형 값을 검색합니다. 열거 형 값이 원래 저장된 AuthenticationMethod와 일치하지 않을 수 있습니다.
deadlydog

좋은 점-비록이 특별한 경우 사람들이 열거 형의 정수 값 (또는 순서를 바꾸는 열거 형 코드)에 의존하지 않기를 바랍니다.이 값은 순전히 스위치로 사용되며 아마도 .Equals () 및의 대안입니다. GetHashCode (). 걱정이
되시면,

=스위치가 작동하도록 운영자에게 과부하 를 걸 수 있습니까? VB 에서이 작업을 수행했으며 이제 select case명령문 에서 사용할 수 있습니다 .
user1318499

@ user1318499 아니요, C #에는 스위치 문에 대해 VB보다 더 엄격한 규칙이 있습니다. Case 문에 클래스 인스턴스를 사용할 수 없습니다. 상수 프리미티브 만 사용할 수 있습니다.
deadlydog

13

위의 제안 중 일부를 캐싱과 결합하여 사용합니다. 이제 그물에서 찾은 코드에서 아이디어를 얻었지만 어디서 얻었는지 기억하지 못합니다. 따라서 누군가가 비슷한 것으로 보이는 것을 발견하면 속성으로 의견을 말하십시오.

어쨌든 사용법에는 유형 변환기가 포함되므로 UI에 바인딩하는 경우 '작동합니다'. 타입 변환기에서 정적 메소드로 초기화하여 빠른 코드 조회를 위해 Jakub의 패턴으로 확장 할 수 있습니다.

기본 사용법은 다음과 같습니다

[TypeConverter(typeof(CustomEnumTypeConverter<MyEnum>))]
public enum MyEnum
{
    // The custom type converter will use the description attribute
    [Description("A custom description")]
    ValueWithCustomDescription,

   // This will be exposed exactly.
   Exact
}

사용자 정의 열거 형 변환기의 코드는 다음과 같습니다.

public class CustomEnumTypeConverter<T> : EnumConverter
    where T : struct
{
    private static readonly Dictionary<T,string> s_toString = 
      new Dictionary<T, string>();

    private static readonly Dictionary<string, T> s_toValue = 
      new Dictionary<string, T>();

    private static bool s_isInitialized;

    static CustomEnumTypeConverter()
    {
        System.Diagnostics.Debug.Assert(typeof(T).IsEnum,
          "The custom enum class must be used with an enum type.");
    }

    public CustomEnumTypeConverter() : base(typeof(T))
    {
        if (!s_isInitialized)
        {
            Initialize();
            s_isInitialized = true;
        }
    }

    protected void Initialize()
    {
        foreach (T item in Enum.GetValues(typeof(T)))
        {
            string description = GetDescription(item);
            s_toString[item] = description;
            s_toValue[description] = item;
        }
    }

    private static string GetDescription(T optionValue)
    {
        var optionDescription = optionValue.ToString();
        var optionInfo = typeof(T).GetField(optionDescription);
        if (Attribute.IsDefined(optionInfo, typeof(DescriptionAttribute)))
        {
            var attribute = 
              (DescriptionAttribute)Attribute.
                 GetCustomAttribute(optionInfo, typeof(DescriptionAttribute));
            return attribute.Description;
        }
        return optionDescription;
    }

    public override object ConvertTo(ITypeDescriptorContext context, 
       System.Globalization.CultureInfo culture, 
       object value, Type destinationType)
    {
        var optionValue = (T)value;

        if (destinationType == typeof(string) && 
            s_toString.ContainsKey(optionValue))
        {
            return s_toString[optionValue];
        }

        return base.ConvertTo(context, culture, value, destinationType);
    }

    public override object ConvertFrom(ITypeDescriptorContext context, 
       System.Globalization.CultureInfo culture, object value)
    {
        var stringValue = value as string;

        if (!string.IsNullOrEmpty(stringValue) && s_toValue.ContainsKey(stringValue))
        {
            return s_toValue[stringValue];
        }

        return base.ConvertFrom(context, culture, value);
    }
}

}


12

귀하의 질문에 당신은 실제로 어디서나 열거 형의 숫자 ​​값이 필요하다고 말한 적이 없습니다.

문자열 유형의 열거 형이 필요하지 않고 (완전한 유형이 아니므로 열거 형의 기초가 될 수 없음) 다음과 같은 방법이 있습니다.

    static class AuthenticationMethod
    {
        public static readonly string
            FORMS = "Forms",
            WINDOWSAUTHENTICATION = "WindowsAuthentication";
    }

enum과 동일한 구문을 사용하여 참조 할 수 있습니다.

if (bla == AuthenticationMethod.FORMS)

숫자 값 (숫자 대신 문자열 비교)보다 약간 느리지 만 플러스 측에서는 반사 (느린)를 사용하여 문자열에 액세스하지 않습니다.


"정적 읽기 전용"대신 "const"를 사용하면 switch 문에서 값을 대소 문자 레이블로 사용할 수 있습니다.
Ed N.

11

확장 방법으로 이것을 해결하는 방법 :

using System.ComponentModel;
public static string GetDescription(this Enum value)
{
    var descriptionAttribute = (DescriptionAttribute)value.GetType()
        .GetField(value.ToString())
        .GetCustomAttributes(false)
        .Where(a => a is DescriptionAttribute)
        .FirstOrDefault();

    return descriptionAttribute != null ? descriptionAttribute.Description : value.ToString();
}

열거 형 :

public enum OrderType
{
    None = 0,
    [Description("New Card")]
    NewCard = 1,
    [Description("Reload")]
    Refill = 2
}

사용법 (여기서 o.OrderType은 열거 형과 이름이 같은 속성 임) :

o.OrderType.GetDescription()

실제 열거 형 값 NewCard 및 Refill 대신 "New Card"또는 "Reload"문자열을 제공합니다.


완전성을 위해 DescriptionAttribute 클래스의 사본을 포함해야합니다.
Bernie White

3
버니, DescriptionAttribute는 System.ComponentModel에
agentnega

11

업데이트 : 8 년 후 C #을 오랫동안 만지지 않은 채이 페이지를 방문하면 더 이상 최선의 해결책이 아닌 것 같습니다. 속성 함수와 연결된 변환기 솔루션이 정말 마음에 듭니다.

이 글을 읽고 있다면 다른 답변도 확인하십시오.
(힌트 : 그들은이 위에 있습니다)


대부분의 사람들 은 Jakub Šturc 가 선택한 답변을 정말로 좋아 했지만 코드를 복사하여 붙여 넣는 것을 싫어하고 가능한 한 적게 해보십시오.

그래서 대부분의 기능이 상속 / 기본 제공되는 EnumBase 클래스를 원했기 때문에 동작 대신 내용에 중점을 두었습니다.

이 방법의 주요 문제는 Enum 값이 형식에 안전한 인스턴스이지만 상호 작용이 Enum 클래스 유형의 정적 구현과 상호 작용한다는 사실에 기반합니다. 제네릭 매직의 약간의 도움으로 마침내 올바른 믹스를 얻었습니다. 누군가가 내가했던 것처럼 이것이 유용하기를 바랍니다.

Jakub의 예제로 시작하지만 상속 및 제네릭을 사용합니다.

public sealed class AuthenticationMethod : EnumBase<AuthenticationMethod, int>
{
    public static readonly AuthenticationMethod FORMS =
        new AuthenticationMethod(1, "FORMS");
    public static readonly AuthenticationMethod WINDOWSAUTHENTICATION =
        new AuthenticationMethod(2, "WINDOWS");
    public static readonly AuthenticationMethod SINGLESIGNON =
        new AuthenticationMethod(3, "SSN");

    private AuthenticationMethod(int Value, String Name)
        : base( Value, Name ) { }
    public new static IEnumerable<AuthenticationMethod> All
    { get { return EnumBase<AuthenticationMethod, int>.All; } }
    public static explicit operator AuthenticationMethod(string str)
    { return Parse(str); }
}

기본 클래스는 다음과 같습니다.

using System;
using System.Collections.Generic;
using System.Linq; // for the .AsEnumerable() method call

// E is the derived type-safe-enum class
// - this allows all static members to be truly unique to the specific
//   derived class
public class EnumBase<E, T> where E: EnumBase<E, T>
{
    #region Instance code
    public T Value { get; private set; }
    public string Name { get; private set; }

    protected EnumBase(T EnumValue, string Name)
    {
        Value = EnumValue;
        this.Name = Name;
        mapping.Add(Name, this);
    }

    public override string ToString() { return Name; }
    #endregion

    #region Static tools
    static private readonly Dictionary<string, EnumBase<E, T>> mapping;
    static EnumBase() { mapping = new Dictionary<string, EnumBase<E, T>>(); }
    protected static E Parse(string name)
    {
        EnumBase<E, T> result;
        if (mapping.TryGetValue(name, out result))
        {
            return (E)result;
        }

        throw new InvalidCastException();
    }
    // This is protected to force the child class to expose it's own static
    // method.
    // By recreating this static method at the derived class, static
    // initialization will be explicit, promising the mapping dictionary
    // will never be empty when this method is called.
    protected static IEnumerable<E> All
    { get { return mapping.Values.AsEnumerable().Cast<E>(); } }
    #endregion
}

기본 정적 생성자에서 파생 된 정적 생성자를 호출 할 수 있습니다. 나는 아직도 그것을 조사하고 있지만, 지금까지 아무런 문제도 발견하지 못했습니다 : stackoverflow.com/questions/55290034/…
Cory-G

10

Keith와 동의하지만 투표 할 수 없습니다 (아직).

정적 메서드와 swith 문을 사용하여 원하는 것을 정확하게 반환합니다. 데이터베이스에서 tinyint를 저장하고 코드에서 실제 열거 형 만 사용하므로 문자열은 UI 요구 사항입니다. 여러 번 테스트 한 결과, 최상의 성능과 출력 제어가 가능해졌습니다.

public static string ToSimpleString(this enum)
{
     switch (enum)
     {
         case ComplexForms:
             return "ComplexForms";
             break;
     }
}

public static string ToFormattedString(this enum)
{
     switch (enum)
     {
         case ComplexForms:
             return "Complex Forms";
             break;
     }
}

그러나 일부 계정에 따르면 유지 보수의 악몽과 코드 냄새가 발생할 수 있습니다. 길고 많은 열거 형 또는 자주 변경되는 열거 형을 계속 지켜보십시오. 그렇지 않으면 이것은 나를위한 훌륭한 솔루션이었습니다.


10

간단한 "Enum"을 구현하려고했지만 값이 정수가 아닌 문자열 인 경우 가장 간단한 해결책은 다음과 같습니다.

    public sealed class MetricValueList
    {
        public static readonly string Brand = "A4082457-D467-E111-98DC-0026B9010912";
        public static readonly string Name = "B5B5E167-D467-E111-98DC-0026B9010912";
    }

이행:

var someStringVariable = MetricValueList.Brand;

2
를 사용하는 대신 변수를 const로 만드는 것이 좋습니다 static readonly.
AndyGeek

1
consts는 컴파일 타임에 구워지기 때문에 공개적으로 액세스 가능한 클래스에는 좋지 않습니다 .cons로 전체 코드를 다시 컴파일하지 않고 타사 DLL을 바꿀 수는 없습니다. consts vs static readonly의 성능 오프셋은 무시할 수 있습니다.
크리스티안 윌리엄스

7

이 문제에 직면했을 때 가장 먼저 답을 찾으려고하는 몇 가지 질문이 있습니다.

  • 열거 형 값의 이름이 목적에 충분히 친숙합니까, 아니면 더 친근한 것을 제공해야합니까?
  • 왕복해야합니까? 즉, 텍스트 값을 가져와 열거 형 값으로 구문 분석해야합니까?
  • 이것은 내 프로젝트의 많은 열거 형 또는 하나에 대해서만해야합니까?
  • 어떤 종류의 UI 요소가이 정보를 제공합니까? 특히 UI에 바인딩하거나 속성 시트를 사용합니까?
  • 현지화가 필요합니까?

가장 간단한 방법은을 사용하는 것입니다 Enum.GetValue(및를 사용하여 라운드 트립 핑 지원 Enum.Parse). TypeConverterSteve Mitcham이 제안한 것처럼 UI 바인딩을 지원하기 위해 종종을 빌드하는 것이 좋습니다. ( TypeConverter속성 시트를 사용할 때 속성 시트를 사용 하는 경우 에는 시간을 쌓을 필요가 없습니다 . 속성 시트의 좋은 점 중 하나입니다. 주님은 자신의 문제가 있음을 알고 있지만)

일반적으로 위의 질문에 대한 답변이 효과가 없다고 제안하는 경우 다음 단계는 static Dictionary<MyEnum, string>또는 가능하면 을 만들고 채우는 것입니다 Dictionary<Type, Dictionary<int, string>>. 일반적으로 다음에 파이크가 내려 오는 것은 배포 후 친숙한 값을 변경해야하기 때문에 (종종 현지화로 인해 항상 그런 것은 아닙니다) 중간 중간 코드 장식-속성 단계를 건너 뛰는 경향이 있습니다.


7

나는 이것을 아래 인용 한 게시물에 대한 의견으로 게시하고 싶었지만 충분한 담당자가 없기 때문에 할 수 없었습니다. 따라서 투표하지 마십시오. 코드에 오류가 포함되어 있으며이 솔루션을 사용하려는 개인에게 이것을 지적하고 싶습니다.

[TypeConverter(typeof(CustomEnumTypeConverter(typeof(MyEnum))]
public enum MyEnum
{
  // The custom type converter will use the description attribute
  [Description("A custom description")]
  ValueWithCustomDescription,
  // This will be exposed exactly.
  Exact
}

해야한다

[TypeConverter(typeof(CustomEnumTypeConverter<MyEnum>))]
public enum MyEnum
{
  // The custom type converter will use the description attribute
  [Description("A custom description")]
  ValueWithCustomDescription,

  // This will be exposed exactly.
  Exact
}

무성한!


5

내 변형

public struct Colors
{
    private String current;

    private static string red = "#ff0000";
    private static string green = "#00ff00";
    private static string blue = "#0000ff";

    private static IList<String> possibleColors; 

    public static Colors Red { get { return (Colors) red; } }
    public static Colors Green { get { return (Colors) green; } }
    public static Colors Blue { get { return (Colors) blue; } }

    static Colors()
    {
        possibleColors = new List<string>() {red, green, blue};
    }

    public static explicit operator String(Colors value)
    {
        return value.current;
    }

    public static explicit operator Colors(String value)
    {
        if (!possibleColors.Contains(value))
        {
            throw new InvalidCastException();
        }

        Colors color = new Colors();
        color.current = value;
        return color;
    }

    public static bool operator ==(Colors left, Colors right)
    {
        return left.current == right.current;
    }

    public static bool operator !=(Colors left, Colors right)
    {
        return left.current != right.current;
    }

    public bool Equals(Colors other)
    {
        return Equals(other.current, current);
    }

    public override bool Equals(object obj)
    {
        if (ReferenceEquals(null, obj)) return false;
        if (obj.GetType() != typeof(Colors)) return false;
        return Equals((Colors)obj);
    }

    public override int GetHashCode()
    {
        return (current != null ? current.GetHashCode() : 0);
    }

    public override string ToString()
    {
        return current;
    }
}

코드는 조금보기 흉한 것처럼 보이지만이 구조체의 사용법은 매우 대표적인 것입니다.

Colors color1 = Colors.Red;
Console.WriteLine(color1); // #ff0000

Colors color2 = (Colors) "#00ff00";
Console.WriteLine(color2); // #00ff00

// Colors color3 = "#0000ff"; // Compilation error
// String color4 = Colors.Red; // Compilation error

Colors color5 = (Colors)"#ff0000";
Console.WriteLine(color1 == color5); // True

Colors color6 = (Colors)"#00ff00";
Console.WriteLine(color1 == color6); // False

또한 많은 열거 형이 필요한 경우 코드 생성 (예 : T4)이 사용될 수 있다고 생각합니다.


4

옵션 1:

public sealed class FormsAuth
{
     public override string ToString{return "Forms Authtentication";}
}
public sealed class WindowsAuth
{
     public override string ToString{return "Windows Authtentication";}
}

public sealed class SsoAuth
{
     public override string ToString{return "SSO";}
}

그리고

object auth = new SsoAuth(); //or whatever

//...
//...
// blablabla

DoSomethingWithTheAuth(auth.ToString());

옵션 2 :

public enum AuthenticationMethod
{
        FORMS = 1,
        WINDOWSAUTHENTICATION = 2,
        SINGLESIGNON = 3
}

public class MyClass
{
    private Dictionary<AuthenticationMethod, String> map = new Dictionary<AuthenticationMethod, String>();
    public MyClass()
    {
         map.Add(AuthenticationMethod.FORMS,"Forms Authentication");
         map.Add(AuthenticationMethod.WINDOWSAUTHENTICATION ,"Windows Authentication");
         map.Add(AuthenticationMethod.SINGLESIGNON ,"SSo Authentication");
    }
}

4

우리가 해결하려는 문제에 대해 생각한다면 그것은 우리가 전혀 필요하지 않은 열거 형이 아닙니다. 우리는 특정 수의 값을 서로 연관시킬 수있는 객체가 필요합니다. 다시 말해 클래스를 정의하는 것입니다.

Jakub Šturc의 type-safe enum 패턴은 내가 보는 최고의 옵션입니다.

보세요 :

  • 전용 생성자가 있으므로 클래스 자체 만 허용되는 값을 정의 할 수 있습니다.
  • 이 클래스는 봉인 된 클래스이므로 상속을 통해 값을 수정할 수 없습니다.
  • 형식이 안전하므로 메서드에 해당 형식 만 필요합니다.
  • 값에 액세스하면 발생하는 반사 성능이 없습니다.
  • 마지막으로 이름, 설명 및 숫자 값과 같이 둘 이상의 필드를 연결하도록 수정할 수 있습니다.

4

나를 위해 실용적인 접근 방식은 클래스 내부 클래스, 샘플입니다.

public class MSEModel
{
    class WITS
    {
        public const string DATE = "5005";
        public const string TIME = "5006";
        public const string MD = "5008";
        public const string ROP = "5075";
        public const string WOB = "5073";
        public const string RPM = "7001";
... 
    }

4

.NET에서 문자열 값 열거 형을 만들기위한 기본 클래스를 만들었습니다. 프로젝트에 복사하여 붙여 넣거나 StringEnum 이라는 NuGet 패키지를 통해 설치할 수있는 하나의 C # 파일입니다 . 깃 허브 레포

  • 클래스에 xml 주석이 주석 처리되어 있으면 Intellisense에서 열거 형 이름을 제안합니다 <completitionlist>. (C #과 VB에서 모두 작동)

Intellisense 데모

  • 일반 열거 형과 비슷한 사용법 :
///<completionlist cref="HexColor"/> 
class HexColor : StringEnum<HexColor>
{
    public static readonly HexColor Blue = Create("#FF0000");
    public static readonly HexColor Green = Create("#00FF00");
    public static readonly HexColor Red = Create("#000FF");
}
    // Static Parse Method
    HexColor.Parse("#FF0000") // => HexColor.Red
    HexColor.Parse("#ff0000", caseSensitive: false) // => HexColor.Red
    HexColor.Parse("invalid") // => throws InvalidOperationException

    // Static TryParse method.
    HexColor.TryParse("#FF0000") // => HexColor.Red
    HexColor.TryParse("#ff0000", caseSensitive: false) // => HexColor.Red
    HexColor.TryParse("invalid") // => null

    // Parse and TryParse returns the preexistent instances
    object.ReferenceEquals(HexColor.Parse("#FF0000"), HexColor.Red) // => true

    // Conversion from your `StringEnum` to `string`
    string myString1 = HexColor.Red.ToString(); // => "#FF0000"
    string myString2 = HexColor.Red; // => "#FF0000" (implicit cast)

설치 :

  • 다음 StringEnum 기본 클래스를 프로젝트에 붙여 넣습니다. ( 최신 버전 )
  • 또는 설치 StringEnum 을 기반으로 NuGet 패키지 .Net Standard 1.0가 실행되도록 .Net Core> = 1.0 .Net Framework> = 4.5 Mono> = 4.6 등
    /// <summary>
    /// Base class for creating string-valued enums in .NET.<br/>
    /// Provides static Parse() and TryParse() methods and implicit cast to string.
    /// </summary>
    /// <example> 
    /// <code>
    /// class Color : StringEnum &lt;Color&gt;
    /// {
    ///     public static readonly Color Blue = Create("Blue");
    ///     public static readonly Color Red = Create("Red");
    ///     public static readonly Color Green = Create("Green");
    /// }
    /// </code>
    /// </example>
    /// <typeparam name="T">The string-valued enum type. (i.e. class Color : StringEnum&lt;Color&gt;)</typeparam>
    public abstract class StringEnum<T> : IEquatable<T> where T : StringEnum<T>, new()
    {
        protected string Value;
        private static Dictionary<string, T> valueDict = new Dictionary<string, T>();
        protected static T Create(string value)
        {
            if (value == null)
                return null; // the null-valued instance is null.

            var result = new T() { Value = value };
            valueDict.Add(value, result);
            return result;
        }

        public static implicit operator string(StringEnum<T> enumValue) => enumValue.Value;
        public override string ToString() => Value;

        public static bool operator !=(StringEnum<T> o1, StringEnum<T> o2) => o1?.Value != o2?.Value;
        public static bool operator ==(StringEnum<T> o1, StringEnum<T> o2) => o1?.Value == o2?.Value;

        public override bool Equals(object other) => this.Value.Equals((other as T)?.Value ?? (other as string));
        bool IEquatable<T>.Equals(T other) => this.Value.Equals(other.Value);
        public override int GetHashCode() => Value.GetHashCode();

        /// <summary>
        /// Parse the <paramref name="value"/> specified and returns a valid <typeparamref name="T"/> or else throws InvalidOperationException.
        /// </summary>
        /// <param name="value">The string value representad by an instance of <typeparamref name="T"/>. Matches by string value, not by the member name.</param>
        /// <param name="caseSensitive">If true, the strings must match case and takes O(log n). False allows different case but is little bit slower (O(n))</param>
        public static T Parse(string value, bool caseSensitive = true)
        {
            var result = TryParse(value, caseSensitive);
            if (result == null)
                throw new InvalidOperationException((value == null ? "null" : $"'{value}'") + $" is not a valid {typeof(T).Name}");

            return result;
        }

        /// <summary>
        /// Parse the <paramref name="value"/> specified and returns a valid <typeparamref name="T"/> or else returns null.
        /// </summary>
        /// <param name="value">The string value representad by an instance of <typeparamref name="T"/>. Matches by string value, not by the member name.</param>
        /// <param name="caseSensitive">If true, the strings must match case. False allows different case but is slower: O(n)</param>
        public static T TryParse(string value, bool caseSensitive = true)
        {
            if (value == null) return null;
            if (valueDict.Count == 0) System.Runtime.CompilerServices.RuntimeHelpers.RunClassConstructor(typeof(T).TypeHandle); // force static fields initialization
            if (caseSensitive)
            {
                if (valueDict.TryGetValue(value, out T item))
                    return item;
                else
                    return null;
            }
            else
            {
                // slower O(n) case insensitive search
                return valueDict.FirstOrDefault(f => f.Key.Equals(value, StringComparison.OrdinalIgnoreCase)).Value;
                // Why Ordinal? => https://esmithy.net/2007/10/15/why-stringcomparisonordinal-is-usually-the-right-choice/
            }
        }
    }

3

다음은 문자열과 열거 형을 연결하는 작업을 수행하는 또 다른 방법입니다.

struct DATABASE {
    public enum enums {NOTCONNECTED, CONNECTED, ERROR}
    static List<string> strings =
        new List<string>() {"Not Connected", "Connected", "Error"};

    public string GetString(DATABASE.enums value) {
        return strings[(int)value];
    }
}

이 메소드는 다음과 같이 호출됩니다.

public FormMain() {
    DATABASE dbEnum;

    string enumName = dbEnum.GetString(DATABASE.enums.NOTCONNECTED);
}

관련 열거 형을 자체 구조체로 그룹화 할 수 있습니다. 이 메서드는 열거 형을 사용하므로 Intellisense를 사용하여 GetString()호출 할 때 열거 형 목록을 표시 할 수 있습니다 .

DATABASE구조체 에서 새 연산자를 선택적으로 사용할 수 있습니다 . 사용 List하지 않으면 첫 번째 GetString()호출이 이루어질 때까지 문자열 이 할당되지 않습니다 .


3

여기에 많은 훌륭한 답변이 있지만 제 경우에는 "string enum"에서 원하는 것을 해결하지 못했습니다.

  1. switch 문에서 사용 가능합니다. 예 : switch (myEnum)
  2. 함수 매개 변수에서 사용할 수 있습니다. 예 : foo (myEnum type)
  3. 예를 들어 myEnum.FirstElement를 참조 할 수 있습니다.
  4. 문자열을 사용할 수 있습니다. 예 : foo ( "FirstElement") == foo (myEnum.FirstElement)

1,2 및 4는 실제로 문자열의 C # Typedef로 해결할 수 있습니다 (문자열은 C #에서 전환 가능하므로)

정적 const 문자열로 3을 해결할 수 있습니다. 따라서 동일한 요구가있는 경우 가장 간단한 방법입니다.

public sealed class Types
{

    private readonly String name;

    private Types(String name)
    {
        this.name = name;

    }

    public override String ToString()
    {
        return name;
    }

    public static implicit operator Types(string str)
    {
        return new Types(str);

    }
    public static implicit operator string(Types str)
    {
        return str.ToString();
    }


    #region enum

    public const string DataType = "Data";
    public const string ImageType = "Image";
    public const string Folder = "Folder";
    #endregion

}

예를 들면 다음과 같습니다.

    public TypeArgs(Types SelectedType)
    {
        Types SelectedType = SelectedType
    }

public TypeObject CreateType(Types type)
    {
        switch (type)
        {

            case Types.ImageType:
              //
                break;

            case Types.DataType:
             //
                break;

        }
    }

문자열 또는 형식으로 CreateType을 호출 할 수 있습니다. 그러나 단점은 모든 문자열이 자동으로 유효한 열거 형 이라는 것입니다.이를 수정할 수는 있지만 일종의 초기화 함수가 필요합니다 ... 또는 명시 적으로 내부 캐스트합니까?

이제 int 값이 (아마도 비교 속도) 당신에게 중요한 경우, 당신은 할 수 야쿱 Šturc 환상적인 대답에서 몇 가지 아이디어를 사용하고 무언가 A가 할 비트 미친, 이것은 나의 자상입니다 :

    public sealed class Types
{
    private static readonly Dictionary<string, Types> strInstance = new Dictionary<string, Types>();
    private static readonly Dictionary<int, Types> intInstance = new Dictionary<int, Types>();

    private readonly String name;
    private static int layerTypeCount = 0;
    private int value;
    private Types(String name)
    {
        this.name = name;
        value = layerTypeCount++;
        strInstance[name] = this;
        intInstance[value] = this;
    }

    public override String ToString()
    {
        return name;
    }


    public static implicit operator Types(int val)
    {
        Types result;
        if (intInstance.TryGetValue(val, out result))
            return result;
        else
            throw new InvalidCastException();
    }

    public static implicit operator Types(string str)
    {
        Types result;
        if (strInstance.TryGetValue(str, out result))
        {
            return result;
        }
        else
        {
            result = new Types(str);
            return result;
        }

    }
    public static implicit operator string(Types str)
    {
        return str.ToString();
    }

    public static bool operator ==(Types a, Types b)
    {
        return a.value == b.value;
    }
    public static bool operator !=(Types a, Types b)
    {
        return a.value != b.value;
    }

    #region enum

    public const string DataType = "Data";
    public const string ImageType = "Image";

    #endregion

}

물론 "bobs = 4; 먼저 포인트를 초기화하지 않으면 의미가 없습니다.

그러나 이론적으로 TypeA == TypeB가 빠를 것입니다 ...


3

내가 당신을 올바르게 이해하고 있다면 간단히 .ToString ()을 사용하여 값에서 열거 형의 이름을 검색 할 수 있습니다 (이미 열거 형으로 캐스팅되었다고 가정). 벌거 벗은 int가 있다면 (데이터베이스 또는 무언가에서 말하면됩니다) 먼저 열거 형에 캐스트 할 수 있습니다. 아래 두 가지 방법 모두 열거 형 이름을 얻습니다.

AuthenticationMethod myCurrentSetting = AuthenticationMethod.FORMS;
Console.WriteLine(myCurrentSetting); // Prints: FORMS
string name = Enum.GetNames(typeof(AuthenticationMethod))[(int)myCurrentSetting-1];
Console.WriteLine(name); // Prints: FORMS

그러나 두 번째 기술은 int를 사용하고 색인이 1을 기반으로한다고 가정합니다 (0이 아닌). GetNames 함수도 비교하면 상당히 무겁습니다. 호출 할 때마다 전체 배열을 생성합니다. 첫 번째 기술에서 볼 수 있듯이 .ToString ()은 실제로 암시 적으로 호출됩니다. 둘 다 이미 답변에 언급되어 있으며, 나는 단지 차이점을 명확히하려고합니다.


3

오래된 게시물이지만 ...

이에 대한 대답은 실제로 매우 간단 할 수 있습니다. 사용 Enum.ToString () 함수를

이 함수에는 6 개의 오버로드가 있습니다. Enum.Tostring ( "F") 또는 Enum.ToString ()을 사용하여 문자열 값을 반환 할 수 있습니다. 다른 것을 귀찮게 할 필요가 없습니다. 작동하는 데모 는 다음과 같습니다.

이 솔루션은 모든 컴파일러에서 작동하지 않을 수도 있지만 ( 이 데모는 예상대로 작동하지 않음 ) 최소한 최신 컴파일러에서는 작동합니다.



2

글쎄, 위의 모든 내용을 읽은 후에는 열거 형을 문자열로 변환하는 문제가 너무 복잡하다고 생각합니다. 열거 된 필드에 속성을 갖는 아이디어가 마음에 들었지만 속성은 주로 메타 데이터에 사용된다고 생각하지만 귀하의 경우 필요한 것은 일종의 현지화라고 생각합니다.

public enum Color 
{ Red = 1, Green = 2, Blue = 3}


public static EnumUtils 
{
   public static string GetEnumResourceString(object enumValue)
    {
        Type enumType = enumValue.GetType();
        string value = Enum.GetName(enumValue.GetType(), enumValue);
        string resourceKey = String.Format("{0}_{1}", enumType.Name, value);
        string result = Resources.Enums.ResourceManager.GetString(resourceKey);
        if (string.IsNullOrEmpty(result))
        {
            result = String.Format("{0}", value);
        }
        return result;
    }
}

위의 메소드를 호출하려고하면이 방법으로 호출 할 수 있습니다

public void Foo()
{
  var col = Color.Red;
  Console.WriteLine (EnumUtils.GetEnumResourceString (col));
}

모든 열거 자 값과 해당 문자열을 포함하는 리소스 파일 만 작성하면됩니다.

자원 이름 자원 값
Color_Red 내 문자열 색상 빨간색
Color_Blue Blueeey
Color_ 녹색 헐크 색상

실제로 매우 좋은 점은 응용 프로그램을 현지화 해야하는 경우 매우 유용하다는 것입니다. 새 언어로 다른 리소스 파일을 만들면됩니다. 그리고 보이라!


1

그런 상황에 처했을 때 아래 해결책을 제안합니다.

그리고 소비 수업으로

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace MyApp.Dictionaries
{
    class Greek
    {

        public static readonly string Alpha = "Alpha";
        public static readonly string Beta = "Beta";
        public static readonly string Gamma = "Gamma";
        public static readonly string Delta = "Delta";


        private static readonly BiDictionary<int, string> Dictionary = new BiDictionary<int, string>();


        static Greek() {
            Dictionary.Add(1, Alpha);
            Dictionary.Add(2, Beta);
            Dictionary.Add(3, Gamma);
            Dictionary.Add(4, Delta);
        }

        public static string getById(int id){
            return Dictionary.GetByFirst(id);
        }

        public static int getByValue(string value)
        {
            return Dictionary.GetBySecond(value);
        }

    }
}

그리고 양방향 사전 사용이 기준 ( https://stackoverflow.com/a/255638/986160 ) 키가 사전에 하나의 값과 연관되고 유사 할 것이라고 가정하면 ( https://stackoverflow.com/a / 255630 / 986160 )이지만 좀 더 우아합니다. 이 사전은 열거 가능하며 정수에서 문자열로 앞뒤로 이동할 수 있습니다. 또한이 클래스를 제외하고 코드베이스에 문자열이 필요하지 않습니다.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Collections;

namespace MyApp.Dictionaries
{

    class BiDictionary<TFirst, TSecond> : IEnumerable
    {
        IDictionary<TFirst, TSecond> firstToSecond = new Dictionary<TFirst, TSecond>();
        IDictionary<TSecond, TFirst> secondToFirst = new Dictionary<TSecond, TFirst>();

        public void Add(TFirst first, TSecond second)
        {
            firstToSecond.Add(first, second);
            secondToFirst.Add(second, first);
        }

        public TSecond this[TFirst first]
        {
            get { return GetByFirst(first); }
        }

        public TFirst this[TSecond second]
        {
            get { return GetBySecond(second); }
        }

        public TSecond GetByFirst(TFirst first)
        {
            return firstToSecond[first];
        }

        public TFirst GetBySecond(TSecond second)
        {
            return secondToFirst[second];
        }

        public IEnumerator GetEnumerator()
        {
            return GetFirstEnumerator();
        }

        public IEnumerator GetFirstEnumerator()
        {
            return firstToSecond.GetEnumerator();
        }

        public IEnumerator GetSecondEnumerator()
        {
            return secondToFirst.GetEnumerator();
        }
    }
}

1

더 큰 문자열 열거 세트의 경우 나열된 예제가 번거로울 수 있습니다. 상태 코드 목록이나 다른 문자열 기반 열거 목록을 원하면 속성 시스템을 사용하는 것이 성 가시고 인스턴스가있는 정적 클래스는 구성하기가 성가시다. 내 자신의 솔루션을 위해 T4 템플릿을 사용하여 문자열 기반 열거 형을 쉽게 만들 수 있습니다. 결과는 HttpMethod 클래스의 작동 방식과 비슷합니다.

다음과 같이 사용할 수 있습니다.

    string statusCode = ResponseStatusCode.SUCCESS; // Automatically converts to string when needed
    ResponseStatusCode codeByValueOf = ResponseStatusCode.ValueOf(statusCode); // Returns null if not found

    // Implements TypeConverter so you can use it with string conversion methods.
    var converter = System.ComponentModel.TypeDescriptor.GetConverter(typeof(ResponseStatusCode));
    ResponseStatusCode code = (ResponseStatusCode) converter.ConvertFromInvariantString(statusCode);

    // You can get a full list of the values
    bool canIterateOverValues = ResponseStatusCode.Values.Any(); 

    // Comparisons are by value of the "Name" property. Not by memory pointer location.
    bool implementsByValueEqualsEqualsOperator = "SUCCESS" == ResponseStatusCode.SUCCESS; 

Enum.tt 파일로 시작합니다.

<#@ include file="StringEnum.ttinclude" #>


<#+
public static class Configuration
{
    public static readonly string Namespace = "YourName.Space";
    public static readonly string EnumName = "ResponseStatusCode";
    public static readonly bool IncludeComments = true;

    public static readonly object Nodes = new
    {
        SUCCESS = "The response was successful.",
        NON_SUCCESS = "The request was not successful.",
        RESOURCE_IS_DISCONTINUED = "The resource requested has been discontinued and can no longer be accessed."
    };
}
#>

그런 다음 StringEnum.ttinclude 파일을 추가하십시오.

<#@ template debug="false" hostspecific="false" language="C#" #>
<#@ assembly name="System.Core" #>
<#@ import namespace="System" #>
<#@ import namespace="System.Linq" #>
<#@ import namespace="System.Text" #>
<#@ import namespace="System.Reflection" #>
<#@ import namespace="System.Collections.Generic" #>
<#@ output extension=".cs" #>
<#@ CleanupBehavior processor="T4VSHost" CleanupAfterProcessingtemplate="true" #>

//------------------------------------------------------------------------------
// <auto-generated>
//     This code was generated by a tool.
//
//     Changes to this file may cause incorrect behavior and will be lost if
//     the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------

using System;
using System.Linq;
using System.Collections.Generic;
using System.ComponentModel;
using System.Globalization;

namespace <#= Configuration.Namespace #>
{
    /// <summary>
    /// TypeConverter implementations allow you to use features like string.ToNullable(T).
    /// </summary>
    public class <#= Configuration.EnumName #>TypeConverter : TypeConverter
    {
        public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
        {
            return sourceType == typeof(string) || base.CanConvertFrom(context, sourceType);
        }

        public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value)
        {
            var casted = value as string;

            if (casted != null)
            {
                var result = <#= Configuration.EnumName #>.ValueOf(casted);
                if (result != null)
                {
                    return result;
                }
            }

            return base.ConvertFrom(context, culture, value);
        }

        public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType)
        {
            var casted = value as <#= Configuration.EnumName #>;
            if (casted != null && destinationType == typeof(string))
            {
                return casted.ToString();
            }

            return base.ConvertTo(context, culture, value, destinationType);
        }
    }

    [TypeConverter(typeof(<#= Configuration.EnumName #>TypeConverter))]
    public class <#= Configuration.EnumName #> : IEquatable<<#= Configuration.EnumName #>>
    {
//---------------------------------------------------------------------------------------------------
// V A L U E S _ L I S T
//---------------------------------------------------------------------------------------------------
<# Write(Helpers.PrintEnumProperties(Configuration.Nodes)); #>

        private static List<<#= Configuration.EnumName #>> _list { get; set; } = null;
        public static List<<#= Configuration.EnumName #>> ToList()
        {
            if (_list == null)
            {
                _list = typeof(<#= Configuration.EnumName #>).GetFields().Where(x => x.IsStatic && x.IsPublic && x.FieldType == typeof(<#= Configuration.EnumName #>))
                    .Select(x => x.GetValue(null)).OfType<<#= Configuration.EnumName #>>().ToList();
            }

            return _list;
        }

        public static List<<#= Configuration.EnumName #>> Values()
        {
            return ToList();
        }

        /// <summary>
        /// Returns the enum value based on the matching Name of the enum. Case-insensitive search.
        /// </summary>
        /// <param name="key"></param>
        /// <returns></returns>
        public static <#= Configuration.EnumName #> ValueOf(string key)
        {
            return ToList().FirstOrDefault(x => string.Compare(x.Name, key, true) == 0);
        }


//---------------------------------------------------------------------------------------------------
// I N S T A N C E _ D E F I N I T I O N
//---------------------------------------------------------------------------------------------------      
        public string Name { get; private set; }
        public string Description { get; private set; }
        public override string ToString() { return this.Name; }

        /// <summary>
        /// Implcitly converts to string.
        /// </summary>
        /// <param name="d"></param>
        public static implicit operator string(<#= Configuration.EnumName #> d)
        {
            return d.ToString();
        }

        /// <summary>
        /// Compares based on the == method. Handles nulls gracefully.
        /// </summary>
        /// <param name="a"></param>
        /// <param name="b"></param>
        /// <returns></returns>
        public static bool operator !=(<#= Configuration.EnumName #> a, <#= Configuration.EnumName #> b)
        {
            return !(a == b);
        }

        /// <summary>
        /// Compares based on the .Equals method. Handles nulls gracefully.
        /// </summary>
        /// <param name="a"></param>
        /// <param name="b"></param>
        /// <returns></returns>
        public static bool operator ==(<#= Configuration.EnumName #> a, <#= Configuration.EnumName #> b)
        {
            return a?.ToString() == b?.ToString();
        }

        /// <summary>
        /// Compares based on the .ToString() method
        /// </summary>
        /// <param name="o"></param>
        /// <returns></returns>
        public override bool Equals(object o)
        {
            return this.ToString() == o?.ToString();
        }

        /// <summary>
        /// Compares based on the .ToString() method
        /// </summary>
        /// <param name="other"></param>
        /// <returns></returns>
        public bool Equals(<#= Configuration.EnumName #> other)
        {
            return this.ToString() == other?.ToString();
        }

        /// <summary>
        /// Compares based on the .Name property
        /// </summary>
        /// <returns></returns>
        public override int GetHashCode()
        {
            return this.Name.GetHashCode();
        }
    }
}

<#+

public static class Helpers
{
        public static string PrintEnumProperties(object nodes)
        {
            string o = "";
            Type nodesTp = Configuration.Nodes.GetType();
            PropertyInfo[] props = nodesTp.GetProperties().OrderBy(p => p.Name).ToArray();

            for(int i = 0; i < props.Length; i++)
            {
                var prop = props[i];
                if (Configuration.IncludeComments)
                {
                    o += "\r\n\r\n";
                    o += "\r\n        ///<summary>";
                    o += "\r\n        /// "+Helpers.PrintPropertyValue(prop, Configuration.Nodes);
                    o += "\r\n        ///</summary>";
                }

                o += "\r\n        public static readonly "+Configuration.EnumName+" "+prop.Name+ " = new "+Configuration.EnumName+"(){ Name = \""+prop.Name+"\", Description = "+Helpers.PrintPropertyValue(prop, Configuration.Nodes)+ "};";
            }

            o += "\r\n\r\n";

            return o;
        }

        private static Dictionary<string, string> GetValuesMap()
        {
            Type nodesTp = Configuration.Nodes.GetType();
            PropertyInfo[] props= nodesTp.GetProperties();
            var dic = new Dictionary<string,string>();
            for(int i = 0; i < props.Length; i++)
            {
                var prop = nodesTp.GetProperties()[i];
                dic[prop.Name] = prop.GetValue(Configuration.Nodes).ToString();
            }
            return dic;
        }

        public static string PrintMasterValuesMap(object nodes)
        {
            Type nodesTp = Configuration.Nodes.GetType();
            PropertyInfo[] props= nodesTp.GetProperties();
            string o = "        private static readonly Dictionary<string, string> ValuesMap = new Dictionary<string, string>()\r\n        {";
            for(int i = 0; i < props.Length; i++)
            {
                var prop = nodesTp.GetProperties()[i];
                o += "\r\n            { \""+prop.Name+"\", "+(Helpers.PrintPropertyValue(prop,Configuration.Nodes)+" },");
            }
            o += ("\r\n        };\r\n");

            return o;
        }


        public static string PrintPropertyValue(PropertyInfo prop, object objInstance)
        {
            switch(prop.PropertyType.ToString()){
                case "System.Double":
                    return prop.GetValue(objInstance).ToString()+"D";
                case "System.Float":
                    return prop.GetValue(objInstance).ToString()+"F";
                case "System.Decimal":
                    return prop.GetValue(objInstance).ToString()+"M";
                case "System.Long":
                    return prop.GetValue(objInstance).ToString()+"L";
                case "System.Boolean":
                case "System.Int16":
                case "System.Int32":
                    return prop.GetValue(objInstance).ToString().ToLowerInvariant();
                case "System.String":
                    return "\""+prop.GetValue(objInstance)+"\"";
            }

            return prop.GetValue(objInstance).ToString();
        }

        public static string _ (int numSpaces)
        {
            string o = "";
            for(int i = 0; i < numSpaces; i++){
                o += " ";
            }

            return o;
        }
}
#>

마지막으로 Enum.tt 파일을 다시 컴파일하면 출력은 다음과 같습니다.

//------------------------------------------------------------------------------
// <auto-generated>
//     This code was generated by a tool.
//
//     Changes to this file may cause incorrect behavior and will be lost if
//     the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------

using System;
using System.Linq;
using System.Collections.Generic;

namespace YourName.Space
{
    public class ResponseStatusCode
    {
//---------------------------------------------------------------------------------------------------
// V A L U E S _ L I S T 
//---------------------------------------------------------------------------------------------------



        ///<summary>
        /// "The response was successful."
        ///</summary>
        public static readonly ResponseStatusCode SUCCESS = new ResponseStatusCode(){ Name = "SUCCESS", Description = "The response was successful."};


        ///<summary>
        /// "The request was not successful."
        ///</summary>
        public static readonly ResponseStatusCode NON_SUCCESS = new ResponseStatusCode(){ Name = "NON_SUCCESS", Description = "The request was not successful."};


        ///<summary>
        /// "The resource requested has been discontinued and can no longer be accessed."
        ///</summary>
        public static readonly ResponseStatusCode RESOURCE_IS_DISCONTINUED = new ResponseStatusCode(){ Name = "RESOURCE_IS_DISCONTINUED", Description = "The resource requested has been discontinued and can no longer be accessed."};


        private static List<ResponseStatusCode> _list { get; set; } = null;
        public static List<ResponseStatusCode> ToList()
        {
            if (_list == null)
            {
                _list = typeof(ResponseStatusCode).GetFields().Where(x => x.IsStatic && x.IsPublic && x.FieldType == typeof(ResponseStatusCode))
                    .Select(x => x.GetValue(null)).OfType<ResponseStatusCode>().ToList();
            }

            return _list;
        }

        public static List<ResponseStatusCode> Values()
        {
            return ToList();
        }

        /// <summary>
        /// Returns the enum value based on the matching Name of the enum. Case-insensitive search.
        /// </summary>
        /// <param name="key"></param>
        /// <returns></returns>
        public static ResponseStatusCode ValueOf(string key)
        {
            return ToList().FirstOrDefault(x => string.Compare(x.Name, key, true) == 0);
        }


//---------------------------------------------------------------------------------------------------
// I N S T A N C E _ D E F I N I T I O N 
//---------------------------------------------------------------------------------------------------       
        public string Name { get; set; }
        public string Description { get; set; }
        public override string ToString() { return this.Name; }

        /// <summary>
        /// Implcitly converts to string.
        /// </summary>
        /// <param name="d"></param>
        public static implicit operator string(ResponseStatusCode d)
        {
            return d.ToString();
        }

        /// <summary>
        /// Compares based on the == method. Handles nulls gracefully.
        /// </summary>
        /// <param name="a"></param>
        /// <param name="b"></param>
        /// <returns></returns>
        public static bool operator !=(ResponseStatusCode a, ResponseStatusCode b)
        {
            return !(a == b);
        }

        /// <summary>
        /// Compares based on the .Equals method. Handles nulls gracefully.
        /// </summary>
        /// <param name="a"></param>
        /// <param name="b"></param>
        /// <returns></returns>
        public static bool operator ==(ResponseStatusCode a, ResponseStatusCode b)
        {
            return a?.ToString() == b?.ToString();
        }

        /// <summary>
        /// Compares based on the .ToString() method
        /// </summary>
        /// <param name="o"></param>
        /// <returns></returns>
        public override bool Equals(object o)
        {
            return this.ToString() == o?.ToString();
        }

        /// <summary>
        /// Compares based on the .Name property
        /// </summary>
        /// <returns></returns>
        public override int GetHashCode()
        {
            return this.Name.GetHashCode();
        }
    }
}
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.