Chuỗi đại diện của một Enum


912

Tôi có bảng liệt kê sau:

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

Tuy nhiên, vấn đề là tôi cần từ "HÌNH THỨC" khi tôi yêu cầu Xác thựcMethod.FORMS chứ không phải id 1.

Tôi đã tìm thấy giải pháp sau đây cho vấn đề này ( liên kết ):

Đầu tiên tôi cần tạo một thuộc tính tùy chỉnh có tên là "StringValue":

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

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

    public string Value
    {
        get { return _value; }
    }

}

Sau đó, tôi có thể thêm thuộc tính này vào điều tra viên của mình:

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

Và tất nhiên tôi cần một cái gì đó để lấy 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;
    }
}

Tốt bây giờ tôi đã có các công cụ để có được một giá trị chuỗi cho một điều tra viên. Sau đó tôi có thể sử dụng nó như thế này:

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

Được rồi bây giờ tất cả những công việc này như một cơ duyên nhưng tôi thấy nó là rất nhiều công việc. Tôi đã tự hỏi nếu có một giải pháp tốt hơn cho việc này.

Tôi cũng đã thử một cái gì đó với một từ điển và thuộc tính tĩnh nhưng điều đó cũng không tốt hơn.


8
Đẹp! Tôi có thể sử dụng điều này để dịch các giá trị enum thành các chuỗi cục bộ.
Øyvind Skaar

5
Mặc dù bạn có thể thấy điều này kéo dài, nhưng đây thực sự là một cách khá linh hoạt để tìm kiếm những thứ khác. Như một trong những đồng nghiệp của tôi đã chỉ ra, điều này có thể được sử dụng trong nhiều trường hợp để thay thế Enum Helpers ánh xạ mã cơ sở dữ liệu thành các giá trị enum, v.v ...
BenAlabaster

27
MSDN nhận các lớp thuộc tính hậu tố với hậu tố "Thuộc tính". Vì vậy, "lớp StringValueAttribution";)
serhio

14
Tôi đồng ý với @BenAlabaster, điều này thực sự khá linh hoạt. Ngoài ra, bạn có thể biến điều này thành một phương thức mở rộng chỉ bằng cách thêm thisvào trước Enumphương thức tĩnh của bạn. Sau đó, bạn có thể làm AuthenticationMethod.Forms.GetStringValue();
Justin Pihony

5
Cách tiếp cận này sử dụng sự phản chiếu để đọc các giá trị thuộc tính và sẽ rất chậm nếu bạn phải gọi GetStringValue () nhiều lần theo kinh nghiệm của tôi. Các mẫu an toàn-enum là nhanh hơn.
Rn222 ngày

Câu trả lời:


868

Hãy thử kiểu mẫu an toàn-enum .

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;
    }

}

Cập nhật chuyển đổi loại rõ ràng (hoặc ẩn) có thể được thực hiện bởi

  • thêm trường tĩnh với ánh xạ

    private static readonly Dictionary<string, AuthenticationMethod> instance = new Dictionary<string,AuthenticationMethod>();
    • nb Để việc khởi tạo các trường "thành viên enum" không ném NullReferenceException khi gọi hàm tạo cá thể, hãy đảm bảo đặt trường Từ điển trước các trường "thành viên enum" trong lớp của bạn. Điều này là do các trình khởi tạo trường tĩnh được gọi theo thứ tự khai báo và trước hàm tạo tĩnh, tạo ra tình huống kỳ lạ và cần thiết nhưng khó hiểu mà hàm tạo cá thể có thể được gọi trước khi tất cả các trường tĩnh được khởi tạo và trước khi hàm tạo tĩnh được gọi.
  • điền vào ánh xạ này trong hàm tạo

    instance[name] = this;
  • và thêm toán tử chuyển đổi loại do người dùng định nghĩa

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

17
Nó trông giống như một enum nhưng nó không phải là một enum. Tôi có thể tưởng tượng rằng việc gây ra một số vấn đề thú vị nếu mọi người bắt đầu thử so sánh xác thực. Bạn có thể cần phải quá tải các nhà khai thác bình đẳng khác nhau quá.
Kiến

36
@Ant: Tôi không phải. Vì chúng ta chỉ có một phiên bản của mỗi Xác thực, nên đẳng thức tham chiếu được kế thừa từ Object hoạt động tốt.
Jakub turc

10
@tyriker: Trình biên dịch nào. Hàm tạo là riêng tư để bạn không thể tạo phiên bản mới. Ngoài ra các thành viên tĩnh không thể truy cập thông qua ví dụ.
Jakub turc

21
@Jakub Rất thú vị. Tôi đã phải chơi với nó để tìm ra cách sử dụng nó, và nhận ra lợi ích của nó. Đó là một lớp công khai, không tĩnh, nhưng không thể được khởi tạo và bạn chỉ có thể truy cập các thành viên tĩnh của nó. Về cơ bản, nó hoạt động như một enum. Nhưng phần tốt nhất ... các thành viên tĩnh được gõ của lớp và không phải là một chuỗi chung hoặc int. Đó là một ... [chờ đợi] ... gõ enum an toàn! Cảm ơn đã giúp tôi hiểu.
ông trùm

6
@kiran Tôi đã đăng một phiên bản sửa đổi một chút của câu trả lời của Jakub Šturc bên dưới cho phép sử dụng nó với các câu lệnh Switch-Case, vì vậy bây giờ không có nhược điểm nào đối với phương pháp này :)
deadlydog

228

Sử dụng phương pháp

Enum.GetName(Type MyEnumType,  object enumvariable)  

như trong (Giả sử Shipperlà một Enum được xác định)

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

Có một loạt các phương thức tĩnh khác trên lớp Enum đáng để nghiên cứu quá ...


5
Chính xác. Tôi đã tạo một thuộc tính tùy chỉnh cho mô tả chuỗi, nhưng đó là vì tôi muốn phiên bản thân thiện với người dùng (có khoảng trắng và các ký tự đặc biệt khác) có thể dễ dàng ràng buộc với ComboBox hoặc tương tự.
lc.

5
Enum.GetName phản ánh tên trường trong enum - giống như .ToString (). Nếu hiệu suất là một vấn đề nó có thể là một vấn đề. Tôi sẽ không lo lắng về điều đó trừ khi bạn chuyển đổi vô số enum.
Keith

8
Một tùy chọn khác để xem xét, nếu bạn cần một enum có thêm dấu phẩy, là "roll yr own" bằng cách sử dụng một cấu trúc ... bạn thêm các thuộc tính có tên chỉ đọc tĩnh để biểu thị các giá trị enum được khởi tạo cho các hàm tạo tạo các thể hiện riêng lẻ của struct ...
Charles Bretana

1
sau đó bạn có thể thêm bất kỳ thành viên cấu trúc nào khác mà bạn muốn, để thực hiện bất kỳ chức năng nào bạn muốn "enum" này có ...
Charles Bretana

2
Vấn đề ở đây là GetName không thể bản địa hóa. Điều đó không phải luôn luôn là một mối quan tâm, nhưng đó là điều cần phải biết.
Joel Coehoorn

79

Bạn có thể tham chiếu tên chứ không phải giá trị bằng cách sử dụng ToString ()

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

Tài liệu ở đây:

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

... và nếu bạn đặt tên cho enum của mình trong Trường hợp Pascal (như tôi làm - chẳng hạn như ThisIsMyEnumValue = 1, v.v.) thì bạn có thể sử dụng biểu thức chính quy rất đơn giản để in biểu mẫu thân thiện:

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

có thể dễ dàng được gọi từ bất kỳ chuỗi nào:

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

Đầu ra:

Chuyển đổi trường hợp Crazy Pascal của tôi thành trường hợp thân thiện

Điều đó giúp tiết kiệm chạy khắp các ngôi nhà tạo ra các thuộc tính tùy chỉnh và gắn chúng vào enum của bạn hoặc sử dụng các bảng tra cứu để kết hợp một giá trị enum với một chuỗi thân thiện và tốt nhất là nó có thể tự quản lý và có thể được sử dụng trên bất kỳ chuỗi Pascal Case nào tái sử dụng nhiều hơn. Tất nhiên, nó không cho phép bạn có một tên thân thiện khác với enum mà giải pháp của bạn cung cấp.

Tôi thích giải pháp ban đầu của bạn mặc dù cho các kịch bản phức tạp hơn mặc dù. Bạn có thể đưa giải pháp của mình thêm một bước nữa và biến GetStringValue của bạn thành phương thức mở rộng của enum và sau đó bạn không cần phải tham chiếu nó như 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;
}

Sau đó, bạn có thể truy cập nó dễ dàng từ ví dụ enum của bạn:

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

2
Điều này không có ích nếu "tên thân thiện" cần một không gian. Chẳng hạn như "Xác thực mẫu"
Ray Booysen

4
Vì vậy, hãy đảm bảo enum được đặt tên bằng các mũ như FormsAuthentication và chèn khoảng trắng trước bất kỳ mũ nào không có ở đầu. Đây không phải là khoa học tên lửa để chèn một khoảng trống trong chuỗi ...
BenAlabaster

4
Khoảng cách tự động của các tên Trường hợp Pascal trở nên có vấn đề nếu chúng chứa các chữ viết tắt nên viết hoa, ví dụ XML hoặc GPS.
Richard Ev

2
@RichardEv, không có regex hoàn hảo cho việc này nhưng đây là một cái nên hoạt động tốt hơn một chút với chữ viết tắt. "(?!^)([^A-Z])([A-Z])", "$1 $2". Vì vậy, HereIsATESTtrở thành Here Is ATEST.
sparebytes

Không thanh lịch làm những "hack" nhỏ đó là những gì họ đang có. Tôi hiểu những gì OP đang nói và tôi đang cố gắng tìm một giải pháp tương tự, ví dụ như sử dụng sự tao nhã của Enums nhưng có thể dễ dàng truy cập vào tin nhắn liên quan. Giải pháp duy nhất tôi có thể nghĩ đến là áp dụng một số loại ánh xạ giữa tên enum và giá trị chuỗi nhưng điều đó không gây ra vấn đề duy trì dữ liệu chuỗi (tuy nhiên nó thực tế cho các kịch bản mà bạn cần có nhiều vùng, v.v. )
Tahir Khalid

72

Thật không may, sự phản chiếu để có được các thuộc tính trên enums khá chậm:

Xem câu hỏi này: Bất cứ ai cũng biết một cách nhanh chóng để có được các thuộc tính tùy chỉnh trên một giá trị enum?

Điều .ToString()này khá chậm trên enums quá.

Bạn có thể viết các phương thức mở rộng cho enums mặc dù:

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

Điều này không tốt, nhưng sẽ nhanh chóng và không yêu cầu sự phản chiếu cho các thuộc tính hoặc tên trường.


Cập nhật C # 6

Nếu bạn có thể sử dụng C # 6 thì mới nameofđiều hành làm việc cho sự đếm, do đó nameof(MyEnum.WINDOWSAUTHENTICATION)sẽ được chuyển đổi sang "WINDOWSAUTHENTICATION"tại thời gian biên dịch , làm cho nó là cách nhanh nhất để có được tên enum.

Lưu ý rằng điều này sẽ chuyển đổi enum rõ ràng thành hằng số nội tuyến, vì vậy nó không hoạt động đối với các enum mà bạn có trong một biến. Vì thế:

nameof(AuthenticationMethod.FORMS) == "FORMS"

Nhưng...

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

24
Bạn có thể tìm nạp các giá trị thuộc tính một lần và đặt chúng vào Từ điển <MyEnum, chuỗi> để giữ khía cạnh khai báo.
Jon Skeet

1
Vâng, đó là những gì chúng tôi đã làm trong một ứng dụng có rất nhiều enum khi chúng tôi phát hiện ra rằng sự phản chiếu là cổ chai.
Keith

Cảm ơn Jon và Keith, tôi đã kết thúc bằng gợi ý từ điển của bạn. Hoạt động tuyệt vời (và nhanh chóng!).
Helge Klein

@JonSkeet Tôi biết cái này cũ. Nhưng làm thế nào một người sẽ đạt được điều này?
dùng919426

2
@ user919426: Đạt được mong muốn? Đưa chúng vào một cuốn từ điển? Chỉ cần tạo một từ điển, lý tưởng với bộ khởi tạo bộ sưu tập ... không rõ bạn đang yêu cầu gì.
Jon Skeet

59

Tôi sử dụng một phương pháp mở rộng:

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();
        }
}

Bây giờ trang trí enumvới:

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

Khi bạn gọi

AuthenticationMethod.FORMS.ToDescription()bạn sẽ nhận được "FORMS".


1
Tôi đã phải thêm using System.ComponentModel;Ngoài ra, phương thức này chỉ hoạt động nếu bạn muốn giá trị Chuỗi giống với tên của Enum. OP muốn một giá trị khác.
elcool

2
Ý bạn là khi bạn gọi AuthenticationMethod.FORMS.ToDescription()?
nicodemus13

41

Chỉ cần sử dụng ToString()phương pháp

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

Để tham chiếu chuỗi Tomato, chỉ cần sử dụng

any.Tomato.ToString();

Ồ Điều đó thật dễ dàng. Tôi biết OP muốn thêm mô tả chuỗi tùy chỉnh, nhưng đây là thứ tôi cần. Tôi nên biết để thử điều này, khi nhìn lại, nhưng tôi đã đi xuống tuyến đường Enum.GetName.
Rafe

7
Tại sao mọi người khác quá phức tạp này?
Brent

18
@Brent Bởi vì hầu hết bạn thường có .ToString()giá trị khác với giá trị thân thiện với người dùng mà bạn cần.
Novitchi S

2
@Brent - bởi vì điều này khác với câu hỏi đang được hỏi. Câu hỏi đang được đặt ra là làm thế nào để bạn có được chuỗi này từ một biến đã được gán một giá trị liệt kê. Đó là năng động trong thời gian chạy. Đây là kiểm tra định nghĩa của loại và đặt trong thời gian chạy.
Hogan

1
@Hogan - ToString () cũng hoạt động trên các biến: any fruit = any.Tomato; string tomato = fruit.ToString();
LiborV

29

Giải pháp rất đơn giản cho vấn đề này với .Net 4.0 trở lên. Không có mã khác là cần thiết.

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

Để có được chuỗi về chỉ cần sử dụng:

MyStatus.Active.ToString("f");

hoặc là

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

Giá trị sẽ là "Hoạt động" hoặc "Lưu trữ".

Để xem các định dạng chuỗi khác nhau ("f" từ phía trên) khi gọi, Enum.ToStringhãy xem trang Chuỗi định dạng liệt kê này


28

Tôi sử dụng thuộc tính Mô tả từ không gian tên System.ComponentModel. Đơn giản chỉ cần trang trí enum và sau đó sử dụng mã này để lấy nó:

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();

        }

Ví dụ:

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

Mã này phục vụ tốt cho các enum khi bạn không cần "Tên thân thiện" và sẽ chỉ trả lại .ToString () của enum.


27

Tôi thực sự thích câu trả lời của Jakub Šturc, nhưng thiếu sót là bạn không thể sử dụng nó với câu lệnh chuyển đổi trường hợp. Đây là một phiên bản sửa đổi một chút của câu trả lời của anh ấy có thể được sử dụng với một câu lệnh chuyển đổi:

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");
}

Vì vậy, bạn nhận được tất cả các lợi ích của câu trả lời của Jakub Šturc, ngoài ra chúng ta có thể sử dụng nó với một câu lệnh chuyển đổi như vậy:

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;      
}

Một giải pháp ngắn hơn sẽ là loại bỏ các enum {} và thay vào đó hãy đếm số lượng tĩnh mà bạn đã xây dựng. Điều này cũng mang lại lợi ích mà bạn không phải thêm một ví dụ mới mà bạn thực hiện vào danh sách enum. ví dụ public static int nextAvailable { get; private set; }sau đó trong hàm tạothis.Value = nextAvailable++;
kjhf

Ý tưởng thú vị @kjhf. Mặc dù vậy, mối quan tâm của tôi là nếu ai đó sắp xếp lại mã, thì giá trị được gán cho các giá trị enum cũng có thể thay đổi. Ví dụ: điều này có thể dẫn đến việc lấy giá trị enum sai khi giá trị enum được lưu vào tệp / cơ sở dữ liệu, thứ tự của các dòng "xác thực mới (...)" được thay đổi (ví dụ: một loại bị xóa), và sau đó chạy lại ứng dụng và lấy giá trị enum từ tệp / cơ sở dữ liệu; giá trị enum có thể không khớp với Xác thựcMethod ban đầu được lưu.
deadlydog

Điểm hay - mặc dù tôi hy vọng trong những trường hợp cụ thể này, mọi người sẽ không dựa vào giá trị nguyên của enum (hoặc sắp xếp lại mã enum.) - và giá trị này hoàn toàn được sử dụng như một công tắc và có thể thay thế cho .Equals () và. GetHashCode (). Nếu quan tâm, bạn luôn có thể đưa ra một nhận xét rất lớn với "KHÔNG GIỚI HẠN": p
kjhf

Bạn có thể quá tải =nhà điều hành để cho phép chuyển đổi để làm việc? Tôi đã làm điều này trong VB và bây giờ có thể sử dụng nó trong select casetuyên bố.
dùng1318499

@ user1318499 Không, C # có các quy tắc chặt chẽ hơn xung quanh câu lệnh chuyển đổi so với VB. Bạn không thể sử dụng các thể hiện lớp cho câu lệnh Case; bạn chỉ có thể sử dụng nguyên thủy không đổi.
deadlydog

13

Tôi sử dụng kết hợp một số gợi ý ở trên, kết hợp với một số bộ nhớ đệm. Bây giờ, tôi đã có ý tưởng từ một số mã mà tôi tìm thấy ở đâu đó trên mạng, nhưng tôi không thể nhớ tôi đã lấy nó ở đâu hoặc tìm thấy nó ở đâu. Vì vậy, nếu bất cứ ai tìm thấy một cái gì đó trông tương tự xin vui lòng bình luận với ghi công.

Dù sao, việc sử dụng liên quan đến các trình chuyển đổi loại, vì vậy nếu bạn ràng buộc với UI thì 'nó chỉ hoạt động'. Bạn có thể mở rộng với mẫu của Jakub để tra cứu mã nhanh bằng cách khởi tạo từ trình chuyển đổi loại thành các phương thức tĩnh.

Việc sử dụng cơ sở sẽ trông như thế này

[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
}

Mã cho trình chuyển đổi loại enum tùy chỉnh sau:

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

Trong câu hỏi của bạn, bạn không bao giờ nói rằng bạn thực sự cần giá trị số của enum ở bất cứ đâu.

Nếu bạn không và chỉ cần một enum của kiểu chuỗi (không phải là kiểu tách rời nên không thể là cơ sở của enum) thì đây là một cách:

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

bạn có thể sử dụng cú pháp tương tự như enum để tham chiếu nó

if (bla == AuthenticationMethod.FORMS)

Nó sẽ chậm hơn một chút so với các giá trị số (so sánh các chuỗi thay vì số) nhưng về mặt tích cực, nó không sử dụng sự phản chiếu (chậm) để truy cập chuỗi.


nếu bạn sử dụng "const" thay vì "static readonly" thì bạn có thể sử dụng các giá trị dưới dạng nhãn case trong câu lệnh switch.
Ed N.

11

Làm thế nào tôi giải quyết điều này như một phương pháp mở rộng:

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();
}

Enum:

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

Cách sử dụng (trong đó o.OrderType là thuộc tính có cùng tên với enum):

o.OrderType.GetDescription()

Cung cấp cho tôi một chuỗi "Thẻ mới" hoặc "Tải lại" thay vì giá trị enum thực tế NewCard và Refill.


Để hoàn thiện, bạn nên bao gồm một bản sao của lớp Mô tả Thuộc tính.
Bernie White

3
Bernie, Mô tả
Thuộc tính

11

Cập nhật: Truy cập trang này, 8 năm sau, sau khi không chạm vào C # trong một thời gian dài, có vẻ như câu trả lời của tôi không còn là giải pháp tốt nhất. Tôi thực sự thích giải pháp chuyển đổi gắn liền với các chức năng thuộc tính.

Nếu bạn đang đọc điều này, hãy chắc chắn rằng bạn cũng kiểm tra các câu trả lời khác.
(gợi ý: họ ở trên cái này)


Như hầu hết các bạn, tôi thực sự thích câu trả lời được chọn bởi Jakub turc , nhưng tôi cũng rất ghét sao chép-dán mã, và cố gắng làm nó ít nhất có thể.

Vì vậy, tôi quyết định tôi muốn có một lớp EnumBase mà từ đó hầu hết các chức năng được kế thừa / tích hợp sẵn, khiến tôi tập trung vào nội dung thay vì hành vi.

Vấn đề chính với cách tiếp cận này dựa trên thực tế là mặc dù các giá trị Enum là các thể hiện an toàn kiểu, nhưng sự tương tác là với việc triển khai Tĩnh của loại Enum Class. Vì vậy, với một chút trợ giúp của ma thuật tướng, tôi nghĩ rằng cuối cùng tôi đã có được sự pha trộn chính xác. Hy vọng ai đó thấy điều này hữu ích như tôi đã làm.

Tôi sẽ bắt đầu với ví dụ của Jakub, nhưng sử dụng tính kế thừa và tổng quát:

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

Và đây là lớp cơ sở:

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
}

Bạn có thể gọi hàm tạo tĩnh dẫn xuất từ ​​hàm tạo tĩnh cơ sở. Tôi vẫn đang xem xét nó, nhưng cho đến nay tôi không tìm thấy vấn đề gì với nó: stackoverflow.com/questions/55290034/ Kẻ
Cory-G

10

Tôi đồng ý với Keith, nhưng tôi không thể bỏ phiếu (chưa).

Tôi sử dụng một phương thức tĩnh và câu lệnh swith để trả về chính xác những gì tôi muốn. Trong cơ sở dữ liệu tôi lưu trữ tinyint và mã của tôi chỉ sử dụng enum thực tế, vì vậy các chuỗi dành cho các yêu cầu UI. Sau nhiều thử nghiệm, điều này dẫn đến hiệu suất tốt nhất và kiểm soát nhiều nhất đối với đầu ra.

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;
     }
}

Tuy nhiên, bởi một số tài khoản, điều này dẫn đến một cơn ác mộng bảo trì có thể và một số mùi mã. Tôi cố gắng để mắt đến những enum dài và rất nhiều enum, hoặc những enum thay đổi thường xuyên. Nếu không, đây đã là một giải pháp tuyệt vời cho tôi.


10

Nếu bạn đến đây để thực hiện một "Enum" đơn giản nhưng có giá trị là chuỗi thay vì ints, thì đây là giải pháp đơn giản nhất:

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

Thực hiện:

var someStringVariable = MetricValueList.Brand;

2
Có lẽ tốt hơn để tạo các biến consts thay vì sử dụng static readonly.
AndyGeek

1
consts không tốt cho các lớp có thể truy cập công khai, vì chúng được nướng trong thời gian biên dịch, bạn không thể thay thế DLL của bên thứ ba mà không biên dịch lại toàn bộ mã của bạn bằng consts. Hiệu suất bù của const so với chỉ đọc tĩnh là không đáng kể.
Kristian Williams

7

Khi tôi đối mặt với vấn đề này, có một vài câu hỏi mà tôi cố gắng tìm câu trả lời trước:

  • Là tên của các giá trị enum của tôi đủ thân thiện cho mục đích, hay tôi cần cung cấp những cái thân thiện hơn?
  • Tôi có cần đi khứ hồi không? Đó là, tôi sẽ cần lấy các giá trị văn bản và phân tích chúng thành các giá trị enum chứ?
  • Đây có phải là thứ tôi cần làm cho nhiều enum trong dự án của tôi không, hay chỉ một cái?
  • Loại yếu tố UI nào tôi sẽ trình bày thông tin này - cụ thể, tôi sẽ ràng buộc với UI hoặc sử dụng trang thuộc tính?
  • Điều này cần phải được bản địa hóa?

Cách đơn giản nhất để làm điều này là với Enum.GetValue(và hỗ trợ sử dụng tính năng ngắt vòng Enum.Parse). Nó cũng thường đáng để xây dựng một TypeConverter, như Steve Mitcham gợi ý, để hỗ trợ ràng buộc UI. (Không cần thiết phải xây dựng TypeConverterkhi bạn sử dụng trang thuộc tính, đây là một trong những điều hay về trang thuộc tính. Mặc dù chúa biết họ có vấn đề riêng.)

Nói chung, nếu câu trả lời cho các câu hỏi trên cho thấy điều đó sẽ không hiệu quả, bước tiếp theo của tôi là tạo và điền vào một tĩnh Dictionary<MyEnum, string>, hoặc có thể là a Dictionary<Type, Dictionary<int, string>>. Tôi có xu hướng bỏ qua bước trang trí trung gian-mã-với-thuộc tính bởi vì điều thường xảy ra tiếp theo là cần phải thay đổi các giá trị thân thiện sau khi triển khai (thường, nhưng không phải luôn luôn, vì nội địa hóa).


7

Tôi muốn đăng bài này dưới dạng bình luận cho bài đăng được trích dẫn bên dưới nhưng không thể vì tôi không có đủ đại diện - vì vậy xin đừng bỏ phiếu. Mã này có lỗi và tôi muốn chỉ ra điều này cho các cá nhân đang cố gắng sử dụng giải pháp này:

[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
}

nên là

[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
}

Sáng chói!


5

Biến thể của tôi

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;
    }
}

Mã trông hơi xấu xí, nhưng cách sử dụng của cấu trúc này là khá trình bày.

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

Ngoài ra, tôi nghĩ rằng, nếu cần rất nhiều enum như vậy, việc tạo mã (ví dụ T4) có thể được sử dụng.


4

Lựa chọn 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";}
}

và sau đó

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

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

DoSomethingWithTheAuth(auth.ToString());

Lựa chọn 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

Nếu bạn nghĩ về vấn đề chúng tôi đang cố gắng giải quyết, thì đó không phải là một enum mà chúng tôi cần. Chúng ta cần một đối tượng cho phép một số giá trị nhất định được liên kết với nhau; nói cách khác, để định nghĩa một lớp.

Mẫu enum an toàn kiểu của Jakub Šturc là lựa chọn tốt nhất tôi thấy ở đây.

Nhìn nó:

  • Nó có một hàm tạo riêng để chỉ bản thân lớp có thể định nghĩa các giá trị được phép.
  • Đây là một lớp niêm phong để các giá trị không thể được sửa đổi thông qua kế thừa.
  • Nó là loại an toàn, cho phép các phương thức của bạn chỉ yêu cầu loại đó.
  • Không có hiệu suất phản ánh phát sinh bằng cách truy cập các giá trị.
  • Và cuối cùng, nó có thể được sửa đổi để liên kết nhiều hơn hai trường với nhau, ví dụ như Tên, Mô tả và Giá trị số.

4

Đối với tôi, cách tiếp cận thực dụng là lớp bên trong lớp, mẫu:

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

Tôi đã tạo một lớp cơ sở để tạo các enum có giá trị chuỗi trong .NET. Nó chỉ là một tệp C # mà bạn có thể sao chép và dán vào các dự án của mình hoặc cài đặt qua gói NuGet có tên StringEnum . GitHub Repo

  • Intellisense sẽ đề xuất tên enum nếu lớp được chú thích với nhận xét xml <completitionlist>. (Hoạt động trong cả C # và VB)

Bản demo của Intellisense

  • Cách sử dụng tương tự như một enum thông thường:
///<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)

Thay thế

  • Dán lớp cơ sở StringEnum sau vào dự án của bạn. ( phiên bản mới nhất )
  • Hoặc cài đặt StringEnum NuGet gói, mà là dựa trên .Net Standard 1.0nên nó chạy trên .Net Core> = 1.0, .Net Framework> = 4.5, Mono> = 4,6, vv
    /// <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

Đây là một cách khác để hoàn thành nhiệm vụ liên kết chuỗi với enums:

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];
    }
}

Phương pháp này được gọi như thế này:

public FormMain() {
    DATABASE dbEnum;

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

Bạn có thể nhóm các enums liên quan trong cấu trúc riêng của họ. Vì phương thức này sử dụng loại enum, bạn có thể sử dụng Intellisense để hiển thị danh sách các enum khi thực hiện GetString()cuộc gọi.

Bạn có thể tùy chọn sử dụng toán tử mới trên DATABASEstruct. Không sử dụng nó có nghĩa là các chuỗi Listkhông được phân bổ cho đến khi GetString()cuộc gọi đầu tiên được thực hiện.


3

Rất nhiều câu trả lời tuyệt vời ở đây nhưng trong trường hợp của tôi không giải quyết được điều tôi muốn từ một "chuỗi enum", đó là:

  1. Có thể sử dụng trong câu lệnh switch, ví dụ: switch (myEnum)
  2. Có thể được sử dụng trong các tham số chức năng, ví dụ như foo (loại myEnum)
  3. Có thể được tham chiếu, ví dụ myEnum.FirstEuity
  4. Tôi có thể sử dụng chuỗi, ví dụ foo ("FirstEuity") == foo (myEnum.FirstEuity)

1,2 & 4 thực sự có thể được giải quyết bằng C # Typedef của một chuỗi (vì các chuỗi có thể chuyển đổi trong c #)

3 có thể được giải quyết bằng chuỗi const tĩnh. Vì vậy, nếu bạn có cùng nhu cầu, đây là cách tiếp cận đơn giản nhất:

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

}

Điều này cho phép ví dụ:

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

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

            case Types.ImageType:
              //
                break;

            case Types.DataType:
             //
                break;

        }
    }

Trong đó CreatType có thể được gọi bằng một chuỗi hoặc một loại. Tuy nhiên, nhược điểm là bất kỳ chuỗi nào tự động là một enum hợp lệ , điều này có thể được sửa đổi nhưng sau đó nó sẽ yêu cầu một số loại hàm init ... hoặc có thể làm cho chúng rõ ràng bỏ nội bộ?

Bây giờ nếu một giá trị int quan trọng đối với bạn (có lẽ là để so sánh tốc độ), bạn có thể sử dụng một số ý tưởng từ câu trả lời tuyệt vời của Jakub Šturc và làm điều gì đó hơi điên rồ, đây là cú đâm của tôi vào nó:

    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

}

nhưng tất nhiên "Các loại bob = 4;" sẽ là vô nghĩa trừ khi bạn đã khởi tạo chúng trước, điều này sẽ đánh bại điểm ...

Nhưng trên lý thuyết TypeA == TypeB sẽ nhanh hơn ...


3

Nếu tôi hiểu bạn một cách chính xác, bạn chỉ cần sử dụng .ToString () để lấy tên của enum từ giá trị (Giả sử nó đã được chọn là Enum); Nếu bạn có int int (giả sử từ cơ sở dữ liệu hoặc thứ gì đó), trước tiên bạn có thể chuyển nó sang enum. Cả hai phương pháp dưới đây sẽ giúp bạn có được tên enum.

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

Hãy nhớ rằng, kỹ thuật thứ hai giả định rằng bạn đang sử dụng ints và chỉ mục của bạn là 1 dựa trên (không phải dựa trên 0). Hàm GetNames cũng khá nặng khi so sánh, bạn đang tạo toàn bộ một mảng mỗi lần nó được gọi. Như bạn có thể thấy trong kỹ thuật đầu tiên, .ToString () thực sự được gọi ngầm. Tất cả những điều này đã được đề cập trong các câu trả lời tất nhiên, tôi chỉ đang cố gắng làm rõ sự khác biệt giữa chúng.


3

bài cũ nhưng ...

Câu trả lời cho điều này thực sự có thể rất đơn giản. Sử dụng Enum.ToString () chức năng

Có 6 lần quá tải của hàm này, bạn có thể sử dụng Enum.Tostring ("F") hoặc Enum.ToString () để trả về giá trị chuỗi. Không cần phải bận tâm với bất cứ điều gì khác. Đây là một bản demo làm việc

Lưu ý rằng giải pháp này có thể không hoạt động cho tất cả các trình biên dịch ( bản demo này không hoạt động như mong đợi ) nhưng ít nhất nó hoạt động cho trình biên dịch mới nhất.


2

dựa trên MSDN: http://msdn.microsoft.com/en-us/l Library / cc138362.aspx

foreach (string str in Enum.GetNames(typeof(enumHeaderField)))
{
    Debug.WriteLine(str);
}

str sẽ là tên của các lĩnh vực


2
Điều này sẽ cho tên của enum, bạn cũng có thể sử dụng ToString () cho điều đó, đây không phải là những gì được yêu cầu. kiểm tra msdn.microsoft.com/en-us/l Library / system.enum.getname.aspx để biết thêm thông tin về bubu của bạn
Mickey Perlstein

2

Chà, sau khi đọc tất cả những điều trên tôi cảm thấy rằng các bạn đã quá phức tạp về vấn đề biến các điều tra viên thành chuỗi. Tôi thích ý tưởng có các thuộc tính trên các trường liệt kê nhưng tôi nghĩ rằng các thuộc tính chủ yếu được sử dụng cho Meta-data, nhưng trong trường hợp của bạn tôi nghĩ rằng tất cả những gì bạn cần là một loại nội địa hóa.

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;
    }
}

Bây giờ nếu chúng ta thử gọi phương thức trên, chúng ta có thể gọi nó theo cách này

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

Tất cả những gì bạn cần làm chỉ là tạo một tệp tài nguyên chứa tất cả các giá trị liệt kê và các chuỗi tương ứng

Tên tài nguyên Giá trị tài nguyên
Color_Red Chuỗi màu của tôi màu đỏ
Color_Blue Blueeey
Color_Green Hulk Màu

Điều thực sự rất hay đó là nó sẽ rất hữu ích nếu bạn cần ứng dụng của mình được bản địa hóa, vì tất cả những gì bạn cần làm chỉ là tạo một tệp tài nguyên khác với ngôn ngữ mới của bạn! và Voe-la!


1

Khi tôi ở trong một tình huống như vậy tôi đề xuất giải pháp dưới đây.

Và là một lớp tiêu thụ, bạn có thể có

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

    }
}

Và sử dụng từ điển hai chiều: Dựa trên điều này ( https://stackoverflow.com/a/255638/986160 ) giả sử rằng các khóa sẽ được liên kết với các giá trị đơn trong từ điển và tương tự như ( https://stackoverflow.com/a / 255630/986160 ) nhưng thanh lịch hơn một chút. Từ điển này cũng có thể đếm được và bạn có thể quay lại từ ints đến chuỗi. Ngoài ra, bạn không cần phải có bất kỳ chuỗi nào trong cơ sở mã của mình ngoại trừ lớp này.

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

Đối với các bộ enum chuỗi lớn hơn, các ví dụ được liệt kê có thể trở nên mệt mỏi. Nếu bạn muốn có một danh sách mã trạng thái hoặc danh sách các enum dựa trên chuỗi khác, một hệ thống thuộc tính gây khó chịu khi sử dụng và một lớp tĩnh với các phiên bản của chính nó gây khó chịu khi định cấu hình. Đối với giải pháp của riêng tôi, tôi sử dụng khuôn mẫu T4 để làm cho việc có các chuỗi được hỗ trợ chuỗi dễ dàng hơn. Kết quả đưa ra tương tự như cách hoạt động của lớp HttpMethod.

Bạn có thể sử dụng nó như thế này:

    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; 

Bạn bắt đầu với một tệp 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."
    };
}
#>

Sau đó, bạn thêm vào tệp StringEnum.ttinclude của bạn.

<#@ 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;
        }
}
#>

Cuối cùng, bạn biên dịch lại tệp Enum.tt của bạn và kết quả đầu ra trông như thế này:

//------------------------------------------------------------------------------
// <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();
        }
    }
}
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.