Làm thế nào để chuyển đổi từ System.Enum sang số nguyên cơ sở?


93

Tôi muốn tạo một phương thức chung để chuyển đổi bất kỳ kiểu dẫn xuất System.Enum nào thành giá trị số nguyên tương ứng của nó mà không cần ép kiểu và tốt nhất là không phân tích cú pháp chuỗi.

Ví dụ, những gì tôi muốn là một cái gì đó như thế này:

// Trivial example, not actually what I'm doing.
class Converter
{
    int ToInteger(System.Enum anEnum)
    {
        (int)anEnum;
    }
}

Nhưng điều này dường như không hoạt động. Resharper báo cáo rằng bạn không thể truyền biểu thức kiểu 'System.Enum' thành kiểu 'int'.

Bây giờ tôi đã nghĩ ra giải pháp này nhưng tôi muốn có một cái gì đó hiệu quả hơn.

class Converter
{
    int ToInteger(System.Enum anEnum)
    {
        return int.Parse(anEnum.ToString("d"));
    }
}

Bất kỳ đề xuất?


1
Tôi tin rằng đó là trình biên dịch đang phàn nàn, không phải Resharper.
Kugel

1
Không cần thiết. Tôi có một phương thức mở rộng trên System.Enum và đôi khi Resharper quyết định khiếu nại: Không thể chuyển đổi loại đối số phiên bản 'Some.Cool.Type.That.Is.An.Enum' thành 'System.Enum' khi nó chắc chắn là một enum. Nếu tôi biên dịch và chạy mã, nó hoạt động tốt. Nếu sau đó tôi tắt VS, thổi bay bộ nhớ cache Resharper và kích hoạt nó sao lưu, mọi thứ sẽ ổn sau khi quét lại xong. Đối với tôi đó là một số loại snafu bộ nhớ cache. Cũng có thể như vậy đối với anh ta.
Mir

@Mir Tôi cũng đã có ReSharper "phàn nàn" về điều này. Cùng một bản sửa lỗi cho tôi. Không chắc tại sao nó bị trộn lẫn các loại này, nhưng nó chắc chắn không phải là trình biên dịch.
akousmata

Câu trả lời:


134

Nếu bạn không muốn truyền,

Convert.ToInt32()

có thể làm các thủ thuật.

Truyền trực tiếp (qua (int)enumValue) là không thể. Lưu ý rằng điều này cũng sẽ là "nguy hiểm" vì một enum có thể có các loại cơ bản khác nhau ( int, long, byte...).

Chính thức hơn: System.Enumkhông có mối quan hệ kế thừa trực tiếp với Int32(mặc dù cả hai đều là ValueTypes), do đó, kết xuất rõ ràng không thể đúng trong hệ thống kiểu


2
Converter.ToInteger(MyEnum.MyEnumConstant);sẽ cung cấp cho bạn không có lỗi ở đây. Hãy chỉnh sửa phần đó.
nawfal

THanks, @nawfal, bạn nói đúng. Tôi chỉnh sửa các câu trả lời (và học được những điều mới mẻ :) ...)
MartinStettner

Không hoạt động nếu cơ bản loại khác vớiint
Alex Zhukovskiy

@YaroslavSivakov nó sẽ không hoạt động, ví dụ như đối với longenum.
Alex Zhukovskiy

System.Enumlà một lớp, vì vậy nó là một kiểu tham chiếu. Giá trị có thể được đóng hộp.
Teejay 20/09/18

42

Tôi đã làm cho nó hoạt động bằng cách truyền đến một đối tượng và sau đó đến một int:

public static class EnumExtensions
{
    public static int ToInt(this Enum enumValue)
    {
        return (int)((object)enumValue);
    }
}

Đây là cách xấu xí và có lẽ không phải là cách tốt nhất. Tôi sẽ tiếp tục làm điều đó, để xem liệu tôi có thể nghĩ ra một cái gì đó tốt hơn ...

CHỈNH SỬA: Tôi vừa chuẩn bị đăng rằng Convert.ToInt32 (enumValue) cũng hoạt động, và nhận thấy rằng MartinStettner đã đánh bại tôi về nó.

public static class EnumExtensions
{
    public static int ToInt(this Enum enumValue)
    {
        return Convert.ToInt32(enumValue);
    }
}

Kiểm tra:

int x = DayOfWeek.Friday.ToInt();
Console.WriteLine(x); // results in 5 which is int value of Friday

CHỈNH SỬA 2: Trong các nhận xét, ai đó nói rằng điều này chỉ hoạt động trong C # 3.0. Tôi vừa thử nghiệm điều này trong VS2005 như thế này và nó hoạt động:

public static class Helpers
{
    public static int ToInt(Enum enumValue)
    {
        return Convert.ToInt32(enumValue);
    }
}

    static void Main(string[] args)
    {
        Console.WriteLine(Helpers.ToInt(DayOfWeek.Friday));
    }

Tôi đã cho bạn +1 nhưng giải pháp này sẽ chỉ hoạt động với C # 3.0 trở lên.
Vadim

Tôi nghĩ, điều này chỉ thỏa mãn trình biên dịch. Bạn có quản lý để kiểm tra điều này trong thời gian chạy không? Tôi không thể chuyển bất kỳ giá trị nào cho hàm này ...
MartinStettner

Vì nó là một phương pháp mở rộng? Hay có điều gì đó khác biệt về enum trong các phiên bản C # cũ hơn?
Miễn phí

Tôi đã thêm một bài kiểm tra vào cuối bài đăng của mình. Tôi đã thử điều đó và nó hiệu quả với tôi.
Miễn phí

@BFree: Nó dường như chỉ hoạt động với các phương thức mở rộng. Tôi đã thử nó với một chức năng "kiểu cũ" (trong C # 2.0) và không quản lý để tìm ra cú pháp để thực sự vượt qua bất kỳ giá trị cho nó (đó là những gì bình luận trước đây của tôi đã về)
MartinStettner

14

Nếu bạn cần chuyển đổi bất kỳ enum nào thành kiểu cơ bản của nó (không phải tất cả các enum đều được hỗ trợ bởi int) thì bạn có thể sử dụng:

return System.Convert.ChangeType(
    enumValue,
    Enum.GetUnderlyingType(enumValue.GetType()));

5
Đây là câu trả lời chống đạn duy nhất.
Rudey

Đây có phải là nguyên nhân khiến quyền anh mở hộp không?
b.ben

@ b.ben vâng. Convert.ChangeTypechấp nhận objectđối số đầu tiên và trả về object.
Drew Noakes

Đúng là tôi thực sự đồng ý với @Rudey về điều này, dù sao thì bạn cũng nên thực hiện kiểm tra hiệu suất. Giải pháp của BFree cho đến nay là nhanh nhất. Nhanh hơn khoảng tám lần. Dù sao chúng ta vẫn đang nói về bọ ve.
20

10

Tại sao bạn cần phải phát minh lại bánh xe bằng phương pháp trợ giúp? Hoàn toàn hợp pháp khi truyền một enumgiá trị cho loại cơ bản của nó.

Nó ít gõ hơn, và theo ý kiến ​​của tôi là dễ đọc hơn, để sử dụng ...

int x = (int)DayOfWeek.Tuesday;

... hơn là những thứ như ...

int y = Converter.ToInteger(DayOfWeek.Tuesday);
// or
int z = DayOfWeek.Tuesday.ToInteger();

6
Tôi muốn chuyển đổi BẤT KỲ giá trị enum nào. Tức là, tôi có nhiều lớp có các trường enum đang được xử lý bởi một số mã theo cách chung chung. Đây là lý do tại sao một phép ép kiểu đơn giản thành int là không thích hợp.
orj

@orj, tôi không thực sự chắc ý của bạn. Bạn có thể đưa ra một ví dụ cho thấy cách sử dụng bạn cần?
LukeH

2
Đôi khi bạn không thể ép kiểu trực tiếp một enum, chẳng hạn như trong trường hợp của một phương thức chung. Bởi vì các phương thức chung không thể bị ràng buộc thành enums, bạn đã ràng buộc nó thành một thứ gì đó như struct, không thể được truyền thành int. Trong trường hợp đó, bạn có thể sử dụng Convert.ToInt32 (enum) để thực hiện.
Daniel T.

Tôi thích ý tưởng rằng phương thức mở rộng ToInteger () tuân theo cùng một định dạng như ToString (). Tôi nghĩ rằng sẽ khó đọc hơn khi ép kiểu sang int khi bạn muốn giá trị int và sử dụng ToString () khi chúng ta cần giá trị chuỗi, đặc biệt là khi mã nằm cạnh nhau.
Louise Eggleton

Ngoài ra, sử dụng phương thức mở rộng có thể thêm tính nhất quán cho cơ sở mã của bạn. Vì, như @nawfal đã chỉ ra, có một số cách để bạn có thể lấy giá trị int từ một enum, tạo một phương thức mở rộng và thực thi việc sử dụng nó có nghĩa là mỗi khi bạn nhận được giá trị int của một enum, bạn đang sử dụng cùng một phương thức
Louise Eggleton

4

Từ câu trả lời của tôi ở đây :

Như etrong:

Enum e = Question.Role;

Sau đó, những công việc này:

int i = Convert.ToInt32(e);
int i = (int)(object)e;
int i = (int)Enum.Parse(e.GetType(), e.ToString());
int i = (int)Enum.ToObject(e.GetType(), e);

Hai cái cuối cùng là xấu xí. Cái đầu tiên sẽ dễ đọc hơn, mặc dù cái thứ hai nhanh hơn nhiều. Hoặc có thể là một phương pháp mở rộng là tốt nhất, tốt nhất của cả hai thế giới.

public static int GetIntValue(this Enum e)
{
    return e.GetValue<int>();
}

public static T GetValue<T>(this Enum e) where T : struct, IComparable, IFormattable, IConvertible, IComparable<T>, IEquatable<T>
{
    return (T)(object)e;
}

Bây giờ bạn có thể gọi:

e.GetValue<int>(); //or
e.GetIntValue();

Bạn có chắc cái thứ hai nhanh hơn không? Tôi giả sử nó sẽ đóng hộp enum và sau đó mở hộp trở lại int?
yoyo

1
@yoyo ebiến đã có phiên bản đóng hộp của kiểu giá trị thực, tức là enum. Khi bạn viết Enum e = Question.Role;nó đã được đóng hộp. Câu hỏi là về việc chuyển đổi boxed System.Enumtrở lại kiểu int bên dưới (unboxing). Vì vậy, ở đây unboxing chỉ là mối quan tâm về hiệu suất. (int)(object)elà một cuộc gọi unbox trực tiếp; có sẽ nhanh hơn các cách tiếp cận khác. Xem này
nawfal

cảm ơn vì lời giải thích. Tôi có ấn tượng nhầm lẫn rằng một phiên bản của System.Enum là một giá trị không được đóng hộp. Cũng xem điều này - msdn.microsoft.com/en-us/library/aa691158(v=vs.71).aspx
yoyo

@yoyo hmm vâng. Trích dẫn có liên quan từ liên kết: Từ bất kỳ kiểu enum nào đến kiểu System.Enum.
nawfal

1

Truyền từ a System.Enumđến một inthoạt động tốt đối với tôi (nó cũng trên MSDN ). Có lẽ đó là một lỗi Resharper.


1
Bạn đang sử dụng phiên bản thời gian chạy nào?
JoshBerke

2
liên kết hiển thị đúc các loại phụ System.Enum, không phải System.Enumchính nó.
nawfal 17/02/13

Một vấn đề tương tự mà tôi gặp phải là lỗi bộ nhớ cache Resharper. Đóng VS và xóa bộ đệm Resharper đã làm cho lỗi biến mất, ngay cả sau khi quét lại toàn bộ.
Mir

1

Vì Enums bị giới hạn ở byte, sbyte, short, ushort, int, uint, long và ulong, chúng ta có thể đưa ra một số giả định.

Chúng tôi có thể tránh ngoại lệ trong quá trình chuyển đổi bằng cách sử dụng vùng chứa lớn nhất hiện có. Thật không may, hộp chứa nào sẽ được sử dụng không rõ ràng vì ulong sẽ phát nổ đối với các số âm và dài sẽ phát nổ đối với các số giữa long.MaxValue và ulong.MaxValue. Chúng ta cần chuyển đổi giữa các lựa chọn này dựa trên loại cơ bản.

Tất nhiên, bạn vẫn cần quyết định phải làm gì khi kết quả không nằm trong một int. Tôi nghĩ việc casting là ổn, nhưng vẫn còn một số vấn đề:

  1. đối với enum dựa trên kiểu có không gian trường lớn hơn int (dài và dài), có thể một số enum sẽ đánh giá cùng một giá trị.
  2. ép kiểu một số lớn hơn int.MaxValue sẽ ném ra một ngoại lệ nếu bạn đang ở trong một vùng được kiểm tra.

Đây là gợi ý của tôi, tôi sẽ để người đọc quyết định nơi để hiển thị chức năng này; như một người trợ giúp hoặc một phần mở rộng.

public int ToInt(Enum e)
{
  unchecked
  {
    if (e.GetTypeCode() == TypeCode.UInt64)
      return (int)Convert.ToUInt64(e);
    else
      return (int)Convert.ToInt64(e);
  }
}

-1

Đừng quên rằng bản thân kiểu Enum có một loạt các hàm trợ giúp tĩnh trong đó. Nếu tất cả những gì bạn muốn làm là chuyển đổi một thể hiện của enum thành kiểu số nguyên tương ứng của nó, thì ép kiểu có lẽ là cách hiệu quả nhất.

Tôi nghĩ rằng ReSharper đang phàn nàn vì Enum không phải là một kiểu liệt kê của bất kỳ kiểu cụ thể nào, và bản thân các phép liệt kê bắt nguồn từ một kiểu giá trị vô hướng, không phải Enum. Nếu bạn cần truyền kiểu thích ứng theo một cách chung chung, tôi sẽ nói điều này có thể phù hợp với bạn (lưu ý rằng bản thân kiểu liệt kê cũng được bao gồm trong kiểu chung:

public static EnumHelpers
{
    public static T Convert<T, E>(E enumValue)
    {
        return (T)enumValue;
    }
}

Điều này sau đó có thể được sử dụng như vậy:

public enum StopLight: int
{
    Red = 1,
    Yellow = 2,
    Green = 3
}

// ...

int myStoplightColor = EnumHelpers.Convert<int, StopLight>(StopLight.Red);

Tôi không thể nói chắc chắn được, nhưng đoạn mã trên thậm chí có thể được hỗ trợ bởi kiểu suy luận của C #, cho phép những điều sau:

int myStoplightColor = EnumHelpers.Convert<int>(StopLight.Red);

1
Thật không may, trong ví dụ của bạn, E có thể là bất kỳ loại nào. Generics không cho phép tôi sử dụng Enum làm ràng buộc tham số kiểu. Tôi không thể nói: ở đâu T: Enum
Vadim

Tuy nhiên, bạn có thể sử dụng where T: struct. Nó không hoàn toàn nghiêm ngặt như bạn muốn, nhưng nó sẽ loại bỏ bất kỳ loại tham chiếu nào (tôi nghĩ đó là những gì bạn đang cố gắng làm.)
jrista

bạn có thể sử dụng: if (! typeof (E) .IsEnum) ném ArgumentException mới ("E phải là kiểu liệt kê");
diadiora

1
Điều này thậm chí không chính xác từ xa, trình trợ giúp tĩnh thậm chí không hợp lệ.
Nicholi

1
Tại sao ai đó nên sử dụng EnumHelpers.Convert<int, StopLight>(StopLight.Red);thay vì (int)StopLight.Read;? Ngoài ra câu hỏi đang nói về System.Enum.
nawfal
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.