Liên kết enum với chuỗi trong C #


363

Tôi biết điều sau đây là không thể vì kiểu liệt kê phải là số nguyên

enum GroupTypes
{
    TheGroup = "OEM",
    TheOtherGroup = "CMB"
}

Từ cơ sở dữ liệu của tôi, tôi nhận được một trường với các mã không toàn diện ( OEMvà và CMB). Tôi muốn làm cho lĩnh vực này thành một enumhoặc một cái gì đó dễ hiểu. Bởi vì nếu mục tiêu là khả năng đọc, giải pháp nên ngắn gọn.

Tôi có những lựa chọn nào khác?


bản sao có thể có của Enum ToString
nawfal

12
Tôi không chắc tại sao hầu hết các câu trả lời không chỉ sử dụng "const chuỗi" và thay vào đó họ đang tạo các lớp tùy chỉnh.
CTS_AE

1
Bạn có thể không thể sử dụng chuỗi, nhưng bạn có thể sử dụng ký tự tốt. Đó là một tùy chọn nếu bạn có thể sử dụng các giá trị một chữ cái.
T. Sar

1
Thực sự bối rối về lý do tại sao giải pháp được đề xuất ở trên bởi CTS_AE thậm chí không nằm trong ba câu trả lời hàng đầu.
Sinjai

@Sinjai Việc phân nhóm rõ ràng các giá trị liên quan sẽ lớn hơn hình phạt của việc mất hiệu suất không thể chấp nhận được, đặc biệt là trong API hoặc thành phần có thể sử dụng lại.
người27

Câu trả lời:


404

Tôi thích sử dụng các thuộc tính trong một lớp thay vì các phương thức, vì chúng trông giống enum hơn.

Đây là một ví dụ cho Logger:

public class LogCategory
{
    private LogCategory(string value) { Value = value; }

    public string Value { get; set; }

    public static LogCategory Trace   { get { return new LogCategory("Trace"); } }
    public static LogCategory Debug   { get { return new LogCategory("Debug"); } }
    public static LogCategory Info    { get { return new LogCategory("Info"); } }
    public static LogCategory Warning { get { return new LogCategory("Warning"); } }
    public static LogCategory Error   { get { return new LogCategory("Error"); } }
}

Truyền các giá trị chuỗi an toàn kiểu làm tham số:

public static void Write(string message, LogCategory logCategory)
{
    var log = new LogEntry { Message = message };
    Logger.Write(log, logCategory.Value);
}

Sử dụng:

Logger.Write("This is almost like an enum.", LogCategory.Info);

4
Tôi chỉ có thể nghĩ ra rằng nó sẽ chậm hơn một chút, nhưng trong hầu hết các trường hợp, điều này sẽ bị bỏ qua. Và nó sẽ không có hành vi tương tự chính xác trong trình soạn thảo. EG: chuyển qua cái này sẽ không tự động điền vào một trường hợp cho mỗi khả năng. Khác với những điểm nhỏ đó, tôi nghĩ rằng đây có lẽ là một giải pháp khá đơn giản.
Boris Callens

3
Và thật dễ dàng để sử dụng Từ điển <LogC Category, Action / Func> làm công tắc. :)
Arnis Lapsa

4
@ArniL. Nó không đủ để hoạt động như khóa, bạn cần ghi đè Equals () và GetHashCode () và bạn muốn đặt thuộc tính Value setter ở chế độ riêng tư. Tuy nhiên, nó không phải là một enum.
Dave Van den Eynde

21
Để sử dụng riêng, tôi đã mở rộng dựa trên khái niệm này, ghi đè ToStringphương thức để trả về Value. Và sau đó cung cấp các toán tử cast ẩn đến và từ một chuỗi. public static implicit operator String(LogCategory category) { return Value; }.
Zarepheth

6
Điều gì về việc sử dụng này trong trường hợp chuyển đổi?
David

175

Bạn cũng có thể sử dụng mô hình mở rộng:

public enum MyEnum
{
    [Description("String 1")]
    V1= 1,
    [Description("String 2")]
    V2= 2
} 

Lớp học mở rộng của bạn

public static class MyEnumExtensions
{
    public static string ToDescriptionString(this MyEnum val)
    {
        DescriptionAttribute[] attributes = (DescriptionAttribute[])val
           .GetType()
           .GetField(val.ToString())
           .GetCustomAttributes(typeof(DescriptionAttribute), false);
        return attributes.Length > 0 ? attributes[0].Description : string.Empty;
    }
} 

sử dụng:

MyEnum myLocal = MyEnum.V1;
print(myLocal.ToDescriptionString());

3
Xem thêm stackoverflow.com/questions/4367723/ cho một tiện ích mở rộng khác và từ chuỗi sang enum bằng cách mô tả.
Dave

15
Tôi không thể không nghĩ rằng việc phản ánh enum mỗi khi bạn muốn hiển thị văn bản nghe có vẻ đau đớn từ góc độ hiệu suất!
Liath

4
@Liath - `.ToString ()` đã sử dụng sự phản chiếu, vì vậy bạn không thực sự mất bất cứ điều gì với cách tiếp cận này và có được sự dễ đọc
James King

1
Bạn có thể tạo phần mở rộng chung để nó được áp dụng tự động cho tất cả các Enums không?
erosebe

3
Để tạo chung, sử dụng public static string ToDescriptionString(this Enum ...tức là không cần gõ rõ ràng MyEnum.
LeeCambl

100

Làm thế nào về việc sử dụng một lớp tĩnh với hằng số?

static class GroupTypes
{
  public const string TheGroup = "OEM";
  public const string TheOtherGroup = "CMB";
}

void DoSomething(string groupType)
{
  if(groupType == GroupTypes.TheGroup)
  {
    // Be nice
  }  
  else if (groupType == GroupTypes.TheOtherGroup)
  {
    // Continue to be nice
  }
  else
  {
    // unexpected, throw exception?
  }
}

9
Đã đồng ý. Tôi gặp khó khăn khi thấy mục đích đằng sau các giải pháp phức tạp hơn, ngoại trừ có thể chuyển sang "enum" kết quả.
fakeleft

@fakeleft bạn không thể sử dụng loại lớp tĩnh với loại chung (mẫu) và có thể các hạn chế khác, tôi nghĩ đó là lý do tại sao mọi người thích các giải pháp "phức tạp hơn".
eselk

2
Các hằng số cần phải là nội bộ hoặc công khai để làm việc này
arviman

46
Các loại tĩnh không thể được sử dụng làm tham số.
Pedro Moreira

2
Như @PedroMoreira chỉ ra, bạn không thể vượt qua GroupTypesdưới dạng loại đối số vì đó là lớp tĩnh. Đó là vấn đề mà ngay cả câu trả lời của Miên cũng giải quyết được. Trong trường hợp này, thay vào đó bạn phải có void DoSomething(string groupType), điều đó có nghĩa là groupTypecó thể có bất kỳ giá trị chuỗi nào , ngay cả các giá trị mà bạn không mong đợi, điều đó có nghĩa là bạn phải chuẩn bị cho các loại không hợp lệ đó và quyết định cách xử lý chúng (ví dụ: bằng cách ném một ngoại lệ). Ngay cả câu trả lời của Miên cũng giải quyết điều đó bằng cách giới hạn số lượng đầu vào hợp lệ vào các tùy chọn được xác định bởi LogCategorylớp.
Pharap

30

Bạn có thể thêm các thuộc tính cho các mục trong bảng liệt kê và sau đó sử dụng sự phản chiếu để lấy các giá trị từ các thuộc tính.

Bạn sẽ phải sử dụng công cụ xác định "trường" để áp dụng các thuộc tính, như vậy:

enum GroupTypes
{
    [field:Description("OEM")]
    TheGroup,

    [field:Description("CMB")]
    TheOtherGroup
}

Sau đó, bạn sẽ phản ánh trên các trường tĩnh của loại enum (trong trường hợp này là GroupTypes) và nhận DescriptionAttributegiá trị bạn đang tìm kiếm bằng cách sử dụng sự phản chiếu:

public static DescriptionAttribute GetEnumDescriptionAttribute<T>(
    this T value) where T : struct
{
    // The type of the enum, it will be reused.
    Type type = typeof(T);

    // If T is not an enum, get out.
    if (!type.IsEnum) 
        throw new InvalidOperationException(
            "The type parameter T must be an enum type.");

    // If the value isn't defined throw an exception.
    if (!Enum.IsDefined(type, value))
        throw new InvalidEnumArgumentException(
            "value", Convert.ToInt32(value), type);

    // Get the static field for the value.
    FieldInfo fi = type.GetField(value.ToString(), 
        BindingFlags.Static | BindingFlags.Public);

    // Get the description attribute, if there is one.
    return fi.GetCustomAttributes(typeof(DescriptionAttribute), true).
        Cast<DescriptionAttribute>().SingleOrDefault();
}

Tôi đã chọn trả lại DescriptionAttributechính nó ở trên, trong trường hợp bạn muốn có thể xác định liệu thuộc tính có được áp dụng hay không.


Mặc dù tôi sẽ ghi nhớ điều này cho các tình huống phức tạp hơn, nhưng nó khá phức tạp đối với một tình huống có mức độ phức tạp của những gì tôi đã nêu trong OP
Boris Callens

26

Bạn có thể làm điều đó rất dễ dàng thực sự. Sử dụng mã sau đây.

enum GroupTypes
{
   OEM,
   CMB
};

Sau đó, khi bạn muốn lấy giá trị chuỗi của từng phần tử enum, chỉ cần sử dụng dòng mã sau đây.

String oemString = Enum.GetName(typeof(GroupTypes), GroupTypes.OEM);

Trước đây tôi đã sử dụng phương pháp này thành công và tôi cũng đã sử dụng một lớp hằng để giữ các hằng chuỗi, cả hai đều hoạt động khá tốt, nhưng tôi có xu hướng thích điều này hơn.


Tôi đã suy nghĩ điều tương tự, nhưng phải có một số điều bắt được điều này ... Nếu không tôi sẽ nghi ngờ nhiều người sẽ đề xuất điều này (Có lẽ tôi chỉ bị hoang tưởng).
Matthijs Wessels

Nắm bắt duy nhất tôi biết về điều này là tôi tin rằng nó sử dụng sự phản chiếu để tìm ra chuỗi. Kết quả là nếu tôi chỉ sau một giải pháp để theo dõi chuỗi không đổi, thì tôi thường sẽ sử dụng một lớp để lưu trữ phần lớn các chuỗi không đổi của mình. Tuy nhiên, nếu tôi gặp tình huống Enum là giải pháp phù hợp (bất kể lấy chuỗi mô tả về các phần tử Enum của tôi), thì thay vì có một chuỗi bổ sung trôi nổi ở đâu đó để quản lý, tôi chỉ sử dụng giá trị enum như được mô tả.
Arthur C

+1 Đây là câu trả lời hay nhất và dễ nhất, và cũng có số phiếu cao ở đây để chứng minh điều đó. Lần duy nhất tốt hơn để sử dụng mô hình mở rộng là khi bạn cần khoảng trắng trong văn bản (chi tiết hơn ở đây ).
SharpC

14
Không, đây chỉ là nhận tên của giá trị enum, không gán chuỗi cho giá trị enum. Mục tiêu của OP là có một chuỗi khác với giá trị enum, ví dụ: Thegroup = "OEM", TheOthergroup = "CMB".
Tim Autin

3
Tôi đồng ý với nhận xét của @ Tim, đây không phải là điều OP đang cố gắng làm. Nếu bạn đang tự hỏi trường hợp sử dụng của trường hợp này là gì, hãy xem xét tình huống trong đó một thiết bị lấy chuỗi làm lệnh, nhưng cũng cần phải có phiên bản lệnh "có thể đọc được" của con người. Tôi cần điều này để liên kết một cái gì đó như "Cập nhật phần sụn" với lệnh "UPDATEFW".
JYelton

20

Hãy thử thêm hằng vào một lớp tĩnh. Bạn không kết thúc với Loại, nhưng bạn sẽ có các hằng số có tổ chức, dễ đọc:

public static class GroupTypes {

    public const string TheGroup = "OEM";
    public const string TheOtherGroup = "CMB";

}

3
Khó đi từ mã trở lại tên mô tả. Bạn sẽ phải sử dụng sự phản chiếu trên tất cả các trường const để tìm kiếm sự trùng khớp.
andleer

1
@andleer Tôi không hiểu mối quan tâm của bạn. Đây là giải pháp tôi sử dụng.
VSO

Vâng, đây thực sự là những gì tôi muốn. Và đây là giải pháp ngắn gọn / thanh lịch nhất mà tôi thấy, giống như tôi đang xác định một giá trị w / int liệt kê - nhưng thay vào đó là các giá trị chuỗi. Hoàn hảo 100%.
Chad

3
Vấn đề với điều này là nó không hoạt động như một Enum theo nghĩa là chúng ta sẽ không có một loại riêng biệt với một danh sách các giá trị hữu hạn. Một hàm mong đợi các hàm này có thể được sử dụng với các chuỗi dạng tự do dễ bị lỗi.
Juan Martinez

14

Tạo một enum thứ hai, cho DB của bạn chứa các mục sau:

enum DBGroupTypes
{
    OEM = 0,
    CMB = 1
}

Bây giờ, bạn có thể sử dụng Enum.Pude để lấy giá trị DBgroupTypes chính xác từ các chuỗi "OEM" và "CMB". Sau đó, bạn có thể chuyển đổi chúng thành int và truy xuất các giá trị chính xác từ phép liệt kê đúng mà bạn muốn sử dụng thêm trong mô hình của mình.


Đây dường như là một bước bổ sung trong quy trình, tại sao không một lớp nào xử lý mọi thứ?
C. Ross

11
Trái ngược với việc sử dụng các thuộc tính và phản ánh?
Dave Van den Eynde

13

Sử dụng một lớp học.

Chỉnh sửa: Ví dụ tốt hơn

class StarshipType
{
    private string _Name;
    private static List<StarshipType> _StarshipTypes = new List<StarshipType>();

    public static readonly StarshipType Ultralight = new StarshipType("Ultralight");
    public static readonly StarshipType Light = new StarshipType("Light");
    public static readonly StarshipType Mediumweight = new StarshipType("Mediumweight");
    public static readonly StarshipType Heavy = new StarshipType("Heavy");
    public static readonly StarshipType Superheavy = new StarshipType("Superheavy");

    public string Name
    {
        get { return _Name; }
        private set { _Name = value; }
    }

    public static IList<StarshipType> StarshipTypes
    {
        get { return _StarshipTypes; }
    }

    private StarshipType(string name, int systemRatio)
    {
        Name = name;
        _StarshipTypes.Add(this);
    }

    public static StarshipType Parse(string toParse)
    {
        foreach (StarshipType s in StarshipTypes)
        {
            if (toParse == s.Name)
                return s;
        }
        throw new FormatException("Could not parse string.");
    }
}

1
Khó đi từ mã trở lại tên mô tả. Bạn sẽ phải sử dụng sự phản chiếu trên tất cả các trường const để tìm kiếm sự trùng khớp.
andleer

1
Tôi thấy điểm của bạn. Tôi sẽ tải lên một phiên bản hoạt động mạnh mẽ sau đó, nhưng tôi thừa nhận nó khá nặng.
C. Ross

Phiên bản của tôi dựa trên stackoverflow
Roman M

7

Một cách khác để giải quyết vấn đề, là có một enum và một chuỗi các chuỗi sẽ ánh xạ các giá trị enum với danh sách các chuỗi:

public enum GroupTypes
{
    TheGroup  = 0,
    TheOtherGroup 
}

string[] GroupTypesStr = {
    "OEM",
    "CMB"
};

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

Log.Write(GroupTypesStr[(int)GroupTypes.TheOtherGroup]);

Nó sẽ nhắc CMB

PROS:

  1. Mã dễ dàng và sạch sẽ.
  2. Hiệu suất cao (đặc biệt so với các phương pháp sử dụng các lớp)

Nhược điểm:

  1. Có xu hướng làm rối danh sách khi chỉnh sửa nó, nhưng nó sẽ ổn cho một danh sách ngắn.

6

Đây là phương thức mở rộng mà tôi đã sử dụng để lấy giá trị enum dưới dạng chuỗi. Đầu tiên ở đây là enum.

public enum DatabaseEnvironment
{
    [Description("AzamSharpBlogDevDatabase")]
    Development = 1, 
    [Description("AzamSharpBlogQADatabase")]
    QualityAssurance = 2, 
    [Description("AzamSharpBlogTestDatabase")] 
    Test = 3
}

Thuộc tính Mô tả đến từ System.ComponentModel.

Và đây là phương pháp mở rộng của tôi:

public static string GetValueAsString(this DatabaseEnvironment environment) 
{
    // get the field 
    var field = environment.GetType().GetField(environment.ToString());
    var customAttributes = field.GetCustomAttributes(typeof (DescriptionAttribute), false);

    if(customAttributes.Length > 0)
    {
        return (customAttributes[0] as DescriptionAttribute).Description;  
    }
    else
    {
        return environment.ToString(); 
    }
}

Bây giờ, bạn có thể truy cập enum dưới dạng giá trị chuỗi bằng mã sau:

[TestFixture]
public class when_getting_value_of_enum
{
    [Test]
    public void should_get_the_value_as_string()
    {
        Assert.AreEqual("AzamSharpBlogTestDatabase",DatabaseEnvironment.Test.GetValueAsString());  
    }
}

5

Bạn đã xem xét một bảng tra cứu bằng cách sử dụng một từ điển?

enum GroupTypes
{
    TheGroup,
    TheOtherGroup
}

Dictionary<string, GroupTypes> GroupTypeLookup = new Dictionary<string, GroupTypes>();
// initialize lookup table:
GroupTypeLookup.Add("OEM", TheGroup);
GroupTypeLookup.Add("CMB", TheOtherGroup);

Sau đó, bạn có thể sử dụng GroupTypeLookup.TryGetValue () để tìm kiếm một chuỗi khi bạn đọc nó.


Làm thế nào để bạn dễ dàng nhận được chìa khóa cho một giá trị nhất định?
eglasius

Câu hỏi không yêu cầu đi theo con đường khác. Nhưng nó đủ đơn giản để xây dựng một từ điển khác theo cách khác. Đó là, Từ điển <GroupTypes, chuỗi>.
Jim Mischel

4
public class DataType
{
    private readonly string value;
    private static readonly Dictionary<string, DataType> predefinedValues;

    public static readonly DataType Json = new DataType("json");
    public static readonly DataType Xml = new DataType("xml");
    public static readonly DataType Text = new DataType("text");
    public static readonly DataType Html = new DataType("html");
    public static readonly DataType Binary = new DataType("binary");

    static DataType()
    {
        predefinedValues = new Dictionary<string, DataType>();
        predefinedValues.Add(Json.Value, Json);
        predefinedValues.Add(Xml.Value, Xml);
        predefinedValues.Add(Text.Value, Text);
        predefinedValues.Add(Html.Value, Html);
        predefinedValues.Add(Binary.Value, Binary);
    }

    private DataType(string value)
    {
        this.value = value;
    }

    public static DataType Parse(string value)
    {
        var exception = new FormatException($"Invalid value for type {nameof(DataType)}");
        if (string.IsNullOrEmpty(value))
            throw exception;

        string key = value.ToLower();
        if (!predefinedValues.ContainsKey(key))
            throw exception;

        return predefinedValues[key];
    }

    public string Value
    {
        get { return value; }
    }
}

3

C # không hỗ trợ chuỗi liệt kê, nhưng trong hầu hết các tình huống, bạn có thể sử dụng Danh sách hoặc Từ điển để có được hiệu ứng mong muốn.

Ví dụ: Để in kết quả đạt / không đạt:

List<string> PassFail = new List<string> { "FAIL", "PASS" };
bool result = true;
Console.WriteLine("Test1: " + PassFail[result.GetHashCode()]);

2

Tôi sẽ làm cho nó vào một lớp để tránh một enum hoàn toàn. Và sau đó với việc sử dụng một bộ gõ, bạn có thể tạo đối tượng khi bạn lấy nó từ db.

I E:

public class Group
{
    public string Value{ get; set; }
    public Group( string value ){ Value = value; } 
    public static Group TheGroup() { return new Group("OEM"); }
    public static Group OtherGroup() { return new Group("CMB"); }

}

2

Tôi sẽ chỉ tạo một từ điển và sử dụng mã làm chìa khóa.

Chỉnh sửa: Để giải quyết nhận xét về việc thực hiện tra cứu ngược (tìm khóa), điều này sẽ không hiệu quả khủng khiếp. Nếu điều này là cần thiết, tôi sẽ viết một lớp mới để xử lý nó.


Ngoài ra bạn có thể dễ dàng lấy một khóa cho một giá trị nhất định?
eglasius

Đối với C.Ross - Tôi không chắc ý của bạn là gì. Bạn có thể đọc các giá trị từ một db và tự động điền từ điển.
jhale

2

Câu hỏi đầu tiên của tôi - Bạn có quyền truy cập vào Cơ sở dữ liệu không? Điều này nên được chuẩn hóa trong cơ sở dữ liệu, lý tưởng là, bất kỳ giải pháp nào cũng sẽ dễ bị lỗi. Theo kinh nghiệm của tôi, các trường dữ liệu đầy "OEM" và "CMB" có xu hướng kết thúc với những thứ như "OEM" và 'dữ liệu tào lao' khác trộn lẫn theo thời gian .... Nếu bạn có thể bình thường hóa nó, bạn có thể sử dụng khóa trong bảng chứa các thành phần là Enum của bạn và bạn đã hoàn thành, với cấu trúc sạch hơn nhiều.

Nếu không có sẵn, tôi sẽ tạo Enum của bạn và tạo một lớp để phân tích chuỗi của bạn thành Enum cho bạn. Điều này ít nhất sẽ cung cấp cho bạn một số linh hoạt trong việc xử lý các mục không chuẩn và linh hoạt hơn nhiều cho việc bẫy hoặc xử lý lỗi so với thực hiện bất kỳ cách giải quyết nào bằng Enum.Pude / Reflection / v.v. Một từ điển sẽ hoạt động, nhưng có thể bị hỏng nếu bạn gặp vấn đề, v.v.

Tôi khuyên bạn nên viết một lớp để bạn có thể làm:

// I renamed this to GroupType, since it sounds like each element has a single type...
GroupType theType = GroupTypeParser.GetGroupType(theDBString);

Điều này bảo tồn hầu hết khả năng đọc của bạn mà không phải thay đổi DB.


2

Nếu tôi hiểu chính xác, bạn cần chuyển đổi từ chuỗi sang enum:

enum GroupTypes {
    Unknown = 0,
    OEM = 1,
    CMB = 2
}
static GroupTypes StrToEnum(string str){
    GroupTypes g = GroupTypes.Unknown;
    try {
        object o = Enum.Parse(typeof(GroupTypes), str, true);
        g = (GroupTypes)(o ?? 0);
    } catch {
    }
    return g;
}
// then use it like this
GroupTypes g1 = StrToEnum("OEM");
GroupTypes g2 = StrToEnum("bad value");

Bạn có thể làm cho nó lạ mắt hơn với tướng cho loại enum nếu bạn muốn.


2

Trong VS 2015, bạn có thể sử dụng nameof

public class LogCategory
{
    public static string Trace;
    public static string Debug;
    public static string Info;
    public static string Warning;
    public static string Error;
}

Sử dụng:

Logger.Write("This is almost like an enum.", nameof(LogCategory.Info));

2

Một điều chỉnh nhỏ đối với phương pháp Mở rộng Glennular, vì vậy bạn có thể sử dụng tiện ích mở rộng cho những thứ khác ngoài chỉ ENUM;

using System;
using System.ComponentModel;
namespace Extensions {
    public static class T_Extensions {
        /// <summary>
        /// Gets the Description Attribute Value
        /// </summary>
        /// <typeparam name="T">Entity Type</typeparam>
        /// <param name="val">Variable</param>
        /// <returns>The value of the Description Attribute or an Empty String</returns>
        public static string Description<T>(this T t) {
            DescriptionAttribute[] attributes = (DescriptionAttribute[])t.GetType().GetField(t.ToString()).GetCustomAttributes(typeof(DescriptionAttribute), false);
            return attributes.Length > 0 ? attributes[0].Description : string.Empty;
        }
    }
}

Hoặc sử dụng Linq

using System;
using System.ComponentModel;
using System.Linq;

namespace Extensions {


public static class T_Extensions {
        public static string Description<T>(this T t) =>
            ((DescriptionAttribute[])t
            ?.GetType()
            ?.GetField(t?.ToString())
            ?.GetCustomAttributes(typeof(DescriptionAttribute), false))
            ?.Select(a => a?.Description)
            ?.FirstOrDefault() 
            ?? string.Empty;  
    }
}

2

Theo câu trả lời của @Even Mien, tôi đã cố gắng đi xa hơn một chút và biến nó thành Chung chung, tôi dường như gần như ở đó nhưng một trường hợp vẫn chống lại và tôi có thể đơn giản hóa mã của mình một chút.
Tôi đăng nó ở đây nếu bất cứ ai thấy tôi có thể cải thiện và đặc biệt là làm cho nó hoạt động như thế nào vì tôi không thể gán nó từ một chuỗi

Cho đến nay tôi có kết quả như sau:

        Console.WriteLine(TestEnum.Test1);//displays "TEST1"

        bool test = "TEST1" == TestEnum.Test1; //true

        var test2 = TestEnum.Test1; //is TestEnum and has value

        string test3 = TestEnum.Test1; //test3 = "TEST1"

        var test4 = TestEnum.Test1 == TestEnum.Test2; //false
         EnumType<TestEnum> test5 = "TEST1"; //works fine

        //TestEnum test5 = "string"; DOESN'T compile .... :(:(

Trường hợp phép thuật xảy ra:

public abstract  class EnumType<T>  where T : EnumType<T>   
{

    public  string Value { get; set; }

    protected EnumType(string value)
    {
        Value = value;
    }


    public static implicit operator EnumType<T>(string s)
    {
        if (All.Any(dt => dt.Value == s))
        {
            Type t = typeof(T);

            ConstructorInfo ci = t.GetConstructor(BindingFlags.Instance | BindingFlags.NonPublic,null, new Type[] { typeof(string) }, null);

            return (T)ci.Invoke(new object[] {s});
        }
        else
        {
            return null;
        }
    }

    public static implicit operator string(EnumType<T> dt)
    {
        return dt?.Value;
    }


    public static bool operator ==(EnumType<T> ct1, EnumType<T> ct2)
    {
        return (string)ct1 == (string)ct2;
    }

    public static bool operator !=(EnumType<T> ct1, EnumType<T> ct2)
    {
        return !(ct1 == ct2);
    }


    public override bool Equals(object obj)
    {
        try
        {
            return (string)obj == Value;
        }
        catch
        {
            return false;
        }
    }

    public override int GetHashCode()
    {
        return Value.GetHashCode();
    }

    public static IEnumerable<T> All
     => typeof(T).GetProperties()
       .Where(p => p.PropertyType == typeof(T))
       .Select(x => (T)x.GetValue(null, null));



}

Tôi chỉ sau đó phải khai báo điều này cho enum của tôi:

public class TestEnum : EnumType<TestEnum> 
{

    private TestEnum(string value) : base(value)
    {}

    public static TestEnum Test1 { get { return new TestEnum("TEST1"); } }
    public static TestEnum Test2 { get { return new TestEnum("TEST2"); } }
}

Cảm ơn bạn cho công việc tuyệt vời này, tôi đã tìm kiếm cách tiếp cận như vậy trong một thời gian dài. Tôi nghĩ bạn sẽ nhận được 1000 điểm cho việc này
user3492977

Ồ cảm ơn bạn đã nhận xét này, và cảm ơn bạn đã nhắc nhở tôi về điều này, tôi đã không sử dụng c # trong hai năm khi tôi viết đoạn mã này, tôi sẽ sớm quay lại với nó!
Lomithrani

@ user3492977 tôi cuối cùng đã trở lại với nó và làm cho nó hoàn toàn functionnal, tôi vẫn còn nghi ngờ mặc dù nếu đó là một ý tưởng tuyệt vời hay là một điều vô ích: D stackoverflow.com/questions/62043138/...
Lomithrani

2

Mới trong .Net Core 3.0 / C # 8.0 (nếu môi trường làm việc của bạn cho phép bạn nâng cấp dự án của mình) là một câu lệnh chuyển đổi tay ngắn có vẻ hơi enum-ish. Vào cuối ngày, đó là cùng một câu lệnh chuyển đổi nhàm chán cũ mà chúng ta đã sử dụng trong nhiều năm.

Chỉ có sự khác biệt thực sự ở đây là tuyên bố chuyển đổi có một bộ đồ mới.

public static RGBColor FromRainbow(Rainbow colorBand) =>
colorBand switch
{
    Rainbow.Red    => new RGBColor(0xFF, 0x00, 0x00),
    Rainbow.Orange => new RGBColor(0xFF, 0x7F, 0x00),
    Rainbow.Yellow => new RGBColor(0xFF, 0xFF, 0x00),
    Rainbow.Green  => new RGBColor(0x00, 0xFF, 0x00),
    Rainbow.Blue   => new RGBColor(0x00, 0x00, 0xFF),
    Rainbow.Indigo => new RGBColor(0x4B, 0x00, 0x82),
    Rainbow.Violet => new RGBColor(0x94, 0x00, 0xD3),
    _              => throw new ArgumentException(message: "invalid enum value", paramName: nameof(colorBand)),
};

Bạn sẽ nhận thấy rằng mã ở trên mà tôi đã sao chép từ đây , thực sự đang sử dụng một enum làm thông số.

Đó không phải là chính xác những gì bạn muốn (và tin tôi đi, tôi đã muốn một cái gì đó tương tự như những gì OP yêu cầu trong một thời gian dài), nhưng tôi thực sự cảm thấy như đây là một nhánh ô liu từ MS. JMO.

Hy vọng nó sẽ giúp được ai đó!


2

Tôi đã sử dụng một cấu trúc như được ám chỉ trong một câu trả lời trước đó, nhưng đã bỏ đi với bất kỳ sự phức tạp nào. Đối với tôi, điều này giống như tạo ra một bảng liệt kê các chuỗi. Nó được sử dụng theo cách tương tự như một phép liệt kê được sử dụng.

    struct ViewTypes
    {
        public const string View1 = "Whatever string you like";
        public const string View2 = "another string";
    }

Ví dụ sử dụng:

   switch( some_string_variable )
   {
      case ViewTypes.View1: /* do something */ break;
      case ViewTypes.View2: /* do something else */ break;
   }

1

Tôi thậm chí đã triển khai một vài enum theo đề xuất của @Even (thông qua class Xpublic static Xcác thành viên), chỉ để tìm hiểu sau đó rằng những ngày này, bắt đầu .Net 4.5, có quyền ToString() phương pháp .

Bây giờ tôi đang thực hiện lại tất cả mọi thứ trở lại enums.


1

Đây là một cách để sử dụng nó như một tham số được gõ mạnh hoặc như một chuỗi :

public class ClassLikeEnum
{
    public string Value
    {
        get;
        private set;
    }

    ClassLikeEnum(string value) 
    {
        Value = value;
    }

    public static implicit operator string(ClassLikeEnum c)
    {
        return c.Value;
    }

    public static readonly ClassLikeEnum C1 = new ClassLikeEnum("RandomString1");
    public static readonly ClassLikeEnum C2 = new ClassLikeEnum("RandomString2");
}

1

Bạn có thể sử dụng hai enum. Một cho cơ sở dữ liệu và một cho cơ sở dễ đọc.

Bạn chỉ cần đảm bảo rằng chúng vẫn đồng bộ, có vẻ như là một chi phí nhỏ. Bạn không phải đặt các giá trị, chỉ cần đặt các vị trí giống nhau, nhưng đặt các giá trị làm cho nó rất rõ ràng hai enum có liên quan và ngăn ngừa lỗi sắp xếp lại các thành viên enum. Và một bình luận cho đội bảo trì biết những thứ này có liên quan và phải được giữ đồng bộ.

// keep in sync with GroupTypes
public enum GroupTypeCodes
{
    OEM,
    CMB
}

// keep in sync with GroupTypesCodes
public enum GroupTypes
{
    TheGroup = GroupTypeCodes.OEM,
    TheOtherGroup = GroupTypeCodes.CMB
}

Để sử dụng nó, bạn chỉ cần chuyển đổi thành mã trước tiên:

GroupTypes myGroupType = GroupTypes.TheGroup;
string valueToSaveIntoDatabase = ((GroupTypeCodes)myGroupType).ToString();

Sau đó, nếu bạn muốn làm cho nó thuận tiện hơn nữa, bạn có thể thêm một chức năng mở rộng chỉ hoạt động cho loại enum này:

public static string ToString(this GroupTypes source)
{
    return ((GroupTypeCodes)source).ToString();
}

và sau đó bạn có thể làm:

GroupTypes myGroupType = GroupTypes.TheGroup;
string valueToSaveIntoDatabase = myGroupType.ToString();

Đó là một thực tiễn xấu: Với một người phụ thuộc, enummột sự thay đổi giá trị dự định trong một người có thể vô tình làm rối tung người khác.
Lorenz Lo Sauer

1

Về cơ bản, tôi đã tìm kiếm câu trả lời Reflection của @ArthurC

Chỉ cần mở rộng câu trả lời của anh ấy một chút, bạn có thể làm cho nó tốt hơn bằng cách có một chức năng chung:

    // If you want for a specific Enum
    private static string EnumStringValue(GroupTypes e)
    {
        return EnumStringValue<GroupTypes>(e);
    }

    // Generic
    private static string EnumStringValue<T>(T enumInstance)
    {
        return Enum.GetName(typeof(T), enumInstance);
    } 

Sau đó, bạn có thể chỉ cần bọc bất cứ điều gì bạn có

EnumStringValue(GroupTypes.TheGroup) // if you incorporate the top part

hoặc là

EnumStringValue<GroupTypes>(GroupTypes.TheGroup) // if you just use the generic

1

Lấy từ @EvenMien và thêm vào một số ý kiến. (Cũng cho trường hợp sử dụng của riêng tôi)

public struct AgentAction
{
    private AgentAction(string value) { Value = value; }

    public string Value { get; private set; }

    public override string ToString()
    {
        return this.Value;
    }

    public static AgentAction Login = new AgentAction("Logout");
    public static AgentAction Logout = new AgentAction("Logout");

    public static implicit operator string(AgentAction action) { return action.ToString(); }
}

1

Thêm lớp này

public class DatabasePreference {
    public DatabasePreference([CallerMemberName] string preferenceName = "") {
        PreferenceName = preferenceName;
    }
    public string PreferenceName;
}

Công việc này đang sử dụng CallerMemberNameđể giảm thiểu mã hóa

Sử dụng:

//Declare names
public static DatabasePreference ScannerDefaultFlashLight = new DatabasePreference();
public static DatabasePreference ScannerQrCodes = new DatabasePreference();
public static DatabasePreference Scanner1dCodes = new DatabasePreference();

Kiểm tra nó:

Console.WriteLine(ScannerDefaultFlashLight.PreferenceName);
Console.WriteLine(ScannerDefaultFlashLight.Scanner1dCodes);

đầu ra:

ScannerDefaultFlashLight
Scanner1dCodes

0

Dựa trên các ý kiến ​​khác, đây là những gì tôi nghĩ ra. Cách tiếp cận này tránh việc phải nhập. Giá trị mà bạn muốn nhận giá trị không đổi.

Tôi có một lớp cơ sở cho tất cả các enum chuỗi như thế này:

using System;
using Newtonsoft.Json;

[JsonConverter(typeof(ConstantConverter))]
public class StringEnum: IConvertible
{
    public string Value { get; set; }

    protected StringEnum(string value)
    {
        Value = value;
    }

    public static implicit operator string(StringEnum c)
    {
        return c.Value;
    }
    public string ToString(IFormatProvider provider)
    {
        return Value;
    }

    public TypeCode GetTypeCode()
    {
        throw new NotImplementedException();
    }

    public bool ToBoolean(IFormatProvider provider)
    {
        throw new NotImplementedException();
    }
    //The same for all the rest of IConvertible methods
}

JsonConverter giống như thế này:

using System;
using Newtonsoft.Json;

class ConstantConverter : JsonConverter
{
    public override bool CanConvert(Type objectType)
    {
        return true;
    }

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        throw new NotImplementedException();
    }

    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
        if (value == null)
        {
            serializer.Serialize(writer, null);
        }
        else
        {
            serializer.Serialize(writer, value.ToString());
        }
    }
}

Và một chuỗi enum thực tế sẽ giống như thế này:

public sealed class Colors : StringEnum
{
    public static Colors Red { get { return new Catalog("Red"); } }
    public static Colors Yellow { get { return new Catalog("Yellow"); } }
    public static Colors White { get { return new Catalog("White"); } }

    private Colors(string value) : base(value) { }
}

Và với điều này, bạn chỉ có thể sử dụng Color.Red để thậm chí tuần tự hóa thành json mà không cần sử dụng thuộc tính Value


0

Tôi không cần bất cứ thứ gì mạnh mẽ như lưu trữ chuỗi trong các thuộc tính. Tôi chỉ cần biến một cái gì đó như MyEnum.BillEveryWeekthành "hóa đơn mỗi tuần" hoặc MyEnum.UseLegacySystemthành "sử dụng hệ thống kế thừa" - về cơ bản phân tách enum bằng vỏ lạc đà của nó thành các từ viết thường.

public static string UnCamelCase(this Enum input, string delimiter = " ", bool preserveCasing = false)
{
    var characters = input.ToString().Select((x, i) =>
    {

       if (i > 0 && char.IsUpper(x))
       {
           return delimiter + x.ToString(CultureInfo.InvariantCulture);
       }
       return x.ToString(CultureInfo.InvariantCulture);

    });

    var result = preserveCasing
       ? string.Concat(characters)
       : string.Concat(characters).ToLower();

    var lastComma = result.LastIndexOf(", ", StringComparison.Ordinal);

    if (lastComma > -1)
    {
       result = result.Remove(lastComma, 2).Insert(lastComma, " and ");
    }

    return result;
}

MyEnum.UseLegacySystem.UnCamelCase() đầu ra "sử dụng hệ thống kế thừa"

Nếu bạn có nhiều cờ được đặt, nó sẽ biến nó thành tiếng Anh đơn giản (được phân cách bằng dấu phẩy trừ dấu "và" thay cho dấu phẩy cuối cùng).

var myCustomerBehaviour = MyEnum.BillEveryWeek | MyEnum.UseLegacySystem | MyEnum.ChargeTaxes;

Console.WriteLine(myCustomerBehaviour.UnCamelCase());
//outputs "bill every week, use legacy system and charge taxes"
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.