วิธีการแปลงจาก System.Enum เป็นจำนวนเต็มฐาน?


96

ฉันต้องการสร้างเมธอดทั่วไปสำหรับการแปลงประเภทที่ได้รับ System.Enum เป็นค่าจำนวนเต็มที่สอดคล้องกันโดยไม่ต้องแคสต์และไม่ควรแยกวิเคราะห์สตริง

เช่นสิ่งที่ฉันต้องการมีดังนี้:

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

แต่ดูเหมือนจะไม่ได้ผล Resharper รายงานว่าคุณไม่สามารถส่งนิพจน์ประเภท 'System.Enum' เพื่อพิมพ์ 'int' ได้

ตอนนี้ฉันคิดวิธีแก้ปัญหานี้ขึ้นมาแล้ว แต่ฉันอยากมีอะไรที่มีประสิทธิภาพมากกว่านี้

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

ข้อเสนอแนะใด ๆ ?


1
ฉันเชื่อว่าเป็นคอมไพเลอร์ที่บ่นไม่ใช่ Resharper
Kugel

1
ไม่จำเป็น. ฉันมีวิธีการขยายใน System.Enum และในบางครั้ง Resharper ก็ตัดสินใจที่จะบ่น: ไม่สามารถแปลงประเภทอาร์กิวเมนต์อินสแตนซ์ 'Some.Cool.Type That.Is.An.Enum' เป็น 'System.Enum' เมื่อไม่ต้องสงสัยเลยว่าเป็น enum ถ้าฉันรวบรวมและเรียกใช้โค้ดมันก็ใช้ได้ดี ถ้าฉันปิด VS แล้วให้ล้างแคช Resharper ออกและทำการสำรองข้อมูลทุกอย่างจะดีเมื่อสแกนใหม่เสร็จแล้ว สำหรับฉันมันเป็นแคช snafu อาจจะเหมือนกันสำหรับเขา
เมีย

@ คุณฉันมี ReSharper "บ่น" เกี่ยวกับเรื่องนี้เช่นกัน การแก้ไขเดียวกันสำหรับฉัน ไม่แน่ใจว่าเหตุใดจึงทำให้ประเภทเหล่านี้ผสมกัน แต่ไม่ใช่คอมไพเลอร์อย่างแน่นอน
akousmata

คำตอบ:


137

หากคุณไม่ต้องการแคสต์

Convert.ToInt32()

สามารถทำเคล็ดลับ

ไม่สามารถแคสต์โดยตรง (ผ่าน(int)enumValue) ได้ หมายเหตุว่าเรื่องนี้ก็จะเป็น "อันตราย" ตั้งแต่ enum สามารถมีประเภทพื้นฐานที่แตกต่างกัน ( int, long, byte... )

อย่างเป็นทางการมากขึ้น: System.Enumไม่มีความสัมพันธ์การสืบทอดโดยตรงกับInt32(แม้ว่าทั้งสองจะเป็นValueTypes) ดังนั้นการร่ายอย่างชัดเจนจึงไม่สามารถถูกต้องได้ภายในระบบประเภท


3
Converter.ToInteger(MyEnum.MyEnumConstant);จะทำให้คุณไม่มีข้อผิดพลาดที่นี่ โปรดแก้ไขส่วนนั้น
nawfal

ขอบคุณ @nawfal คุณพูดถูก ฉันแก้ไขคำตอบ (และเรียนรู้สิ่งใหม่ ๆ :) ... )
MartinStettner

ไม่ทำงานหากประเภทพื้นฐานแตกต่างจากint
Alex Zhukovskiy

@YaroslavSivakov มันจะไม่ทำงานเช่นสำหรับlongenum
Alex Zhukovskiy

System.Enumเป็นคลาสดังนั้นจึงเป็นประเภทอ้างอิง ค่าอาจจะอยู่ในกล่อง
Teejay

45

ฉันทำให้มันทำงานได้โดยการส่งไปยังวัตถุแล้วไปที่ int:

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

นี่เป็นเรื่องน่าเกลียดและอาจไม่ใช่วิธีที่ดีที่สุด ฉันจะยุ่งกับมันเพื่อดูว่าฉันจะสามารถหาสิ่งที่ดีกว่าได้ไหม ....

แก้ไข: กำลังจะโพสต์ว่า Convert ToInt32 (enumValue) ใช้งานได้เช่นกันและสังเกตว่า MartinStettner เอาชนะฉันได้

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

ทดสอบ:

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

แก้ไข 2: ในความคิดเห็นมีคนบอกว่าใช้ได้เฉพาะใน C # 3.0 ฉันเพิ่งทดสอบสิ่งนี้ใน VS2005 เช่นนี้และใช้งานได้:

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

ฉันให้ +1 แก่คุณ แต่โซลูชันนี้จะใช้ได้เฉพาะกับ C # 3.0 ขึ้นไป
Vadim

ฉันคิดว่าสิ่งนี้ตอบสนองความต้องการของคอมไพเลอร์เท่านั้น คุณจัดการทดสอบสิ่งนี้ที่รันไทม์หรือไม่? ฉันไม่สามารถส่งผ่านค่าใด ๆ ไปยังฟังก์ชันนี้ได้ ...
MartinStettner

เพราะมันเป็นวิธีการขยาย? หรือมีบางอย่างที่แตกต่างกันเกี่ยวกับ enums ใน C # เวอร์ชันเก่าหรือไม่?
BFree

ฉันได้เพิ่มการทดสอบที่ส่วนท้ายของโพสต์ของฉัน ฉันลองแล้วและได้ผลสำหรับฉัน
BFree

@BFree: ดูเหมือนว่าจะใช้ได้กับวิธีการขยายเท่านั้น ฉันลองใช้ฟังก์ชัน "แบบเก่า" (ใน C # 2.0) และไม่สามารถหาไวยากรณ์เพื่อส่งผ่านค่าใด ๆ ไปได้จริง ๆ (นั่นคือความคิดเห็นก่อนหน้าของฉันเกี่ยวกับ)
MartinStettner

15

หากคุณจำเป็นต้องแปลงใด ๆ enum ประเภทพื้นฐานของ (ไม่ enums ทั้งหมดได้รับการสนับสนุนโดยint) แล้วคุณสามารถใช้:

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

5
นี่คือคำตอบแบบกันกระสุนเท่านั้น
Rudey

นี่คือสาเหตุของการแกะกล่องมวยหรือไม่?
เบน

@ b.ben ใช่ Convert.ChangeTypeยอมรับสำหรับอาร์กิวเมนต์แรกและผลตอบแทนobject object
Drew Noakes

แท้จริงแล้วฉันเห็นด้วยกับ @Rudey ในเรื่องนี้อย่างไรก็ตามคุณควรทำการทดสอบประสิทธิภาพ วิธีแก้ปัญหาของ BFree นั้นเร็วที่สุด เร็วขึ้นประมาณแปดเท่า อย่างไรก็ตามเรายังคงพูดถึงเห็บ
ยี่สิบ

10

ทำไมคุณต้องสร้างล้อใหม่ด้วยวิธีการช่วยเหลือ? เป็นเรื่องถูกกฎหมายอย่างสมบูรณ์ที่จะสร้างenumมูลค่าให้กับประเภทพื้นฐาน

มันพิมพ์น้อยลงและในความคิดของฉันอ่านง่ายกว่าที่จะใช้ ...

int x = (int)DayOfWeek.Tuesday;

... มากกว่าสิ่งที่ชอบ ...

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

6
ฉันต้องการแปลงค่า enum ใด ๆ กล่าวคือฉันมีคลาสมากมายที่มีฟิลด์ enum ที่กำลังประมวลผลโดยโค้ดบางส่วนในลักษณะทั่วไป นี่คือเหตุผลว่าทำไมการร่ายแบบธรรมดาถึง int จึงไม่เหมาะสม
orj

@orj ฉันไม่แน่ใจจริงๆว่าคุณหมายถึงอะไร คุณสามารถยกตัวอย่างการใช้งานที่คุณต้องการได้หรือไม่?
LukeH

2
บางครั้งคุณไม่สามารถร่าย enum ได้โดยตรงเช่นในกรณีของวิธีการทั่วไป เนื่องจากวิธีการทั่วไปไม่สามารถ จำกัด enums ได้คุณจึง จำกัด มันเป็นบางอย่างเช่น struct ซึ่งไม่สามารถแปลงเป็น int ได้ ในกรณีนั้นคุณสามารถใช้ Convert ToInt32 (enum) ได้
Daniel T.

ฉันชอบความคิดที่ว่าวิธีการขยาย ToInteger () เป็นไปตามรูปแบบเดียวกับ ToString () ฉันคิดว่ามันอ่านได้น้อยกว่าที่จะส่งไปยัง int ในมือเดียวเมื่อคุณต้องการค่า int และใช้ ToString () เมื่อเราต้องการค่าสตริงโดยเฉพาะเมื่อโค้ดอยู่เคียงข้างกัน
Louise Eggleton

นอกจากนี้การใช้วิธีการขยายสามารถเพิ่มความสอดคล้องให้กับฐานรหัสของคุณ เนื่องจากตามที่ @nawfal ชี้ให้เห็นมีหลายวิธีที่คุณจะได้รับค่า int จาก enum การสร้างวิธีการขยายและบังคับใช้หมายความว่าทุกครั้งที่คุณได้รับค่า int ของ enum คุณจะใช้วิธีการเดียวกัน
Louise Eggleton

4

จากคำตอบของฉันที่นี่ :

ระบุeไว้ใน:

Enum e = Question.Role;

จากนั้นงานเหล่านี้:

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

สองคนสุดท้ายน่าเกลียดธรรมดา อันแรกควรอ่านได้มากกว่าแม้ว่าอันที่สองจะเร็วกว่ามาก หรืออาจเป็นวิธีการขยายที่ดีที่สุดดีที่สุดของทั้งสองโลก

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

ตอนนี้คุณสามารถโทร:

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

แน่ใจไหมว่าอันที่สองเร็วกว่า ฉันคิดว่ามันจะใส่กล่อง enum แล้วแกะกล่องกลับไปที่ int?
โยโย่

1
@yoyo eตัวแปรมีอินสแตนซ์ชนิดกล่องของชนิดค่าจริงอยู่แล้วเช่น enum เมื่อคุณเขียนEnum e = Question.Role;มันมีกล่องอยู่แล้ว คำถามเกี่ยวกับการแปลงชนิดกล่องSystem.Enumกลับไปเป็นประเภท int ที่อยู่ภายใต้ (unboxing) ดังนั้นที่นี่การแกะกล่องจึงเป็นเพียงปัญหาด้านประสิทธิภาพเท่านั้น (int)(object)eเป็นการโทรแบบ unbox โดยตรง ใช่ควรเร็วกว่าวิธีอื่น ๆ ดูสิ่งนี้
nawfal

ขอบคุณสำหรับคำอธิบาย ฉันรู้สึกผิดพลาดที่อินสแตนซ์ของ System.Enum เป็นค่าที่ไม่มีกล่อง ดูสิ่งนี้ด้วย - msdn.microsoft.com/en-us/library/aa691158(v=vs.71).aspx
yoyo

@yoyo อืมใช่ คำพูดที่เกี่ยวข้องจากลิงค์: From any enum-type to the type System.Enum.
nawfal

1

การแคสต์จาก a System.Enumถึงa intทำงานได้ดีสำหรับฉัน (มันอยู่ในMSDN ด้วย ) บางทีอาจเป็นข้อผิดพลาดของ Resharper


1
คุณใช้รันไทม์เวอร์ชันใด
JoshBerke

2
ลิงก์แสดงการแคสต์ประเภทย่อยSystem.Enumไม่ใช่System.Enumตัวเอง
nawfal

ปัญหาที่คล้ายกันที่ฉันพบคือ snafu แคช Resharper การปิด VS และการลบแคช Resharper ทำให้ข้อผิดพลาดหายไปแม้ว่าจะสแกนซ้ำแบบเต็มแล้วก็ตาม
เมีย

1

เนื่องจาก Enums ถูก จำกัด ไว้ที่ byte, sbyte, short, ushort, int, uint, long และ ulong เราจึงสามารถตั้งสมมติฐานได้

เราสามารถหลีกเลี่ยงข้อยกเว้นระหว่างการแปลงได้โดยใช้คอนเทนเนอร์ที่ใหญ่ที่สุดที่มีอยู่ น่าเสียดายที่คอนเทนเนอร์ที่จะใช้นั้นไม่ชัดเจนเพราะ ulong จะระเบิดเป็นจำนวนลบและ long จะระเบิดเป็นตัวเลขระหว่าง long MaxValue และ ulong.MaxValue เราจำเป็นต้องสลับระหว่างตัวเลือกเหล่านี้ตามประเภทพื้นฐาน

แน่นอนคุณยังคงต้องตัดสินใจว่าจะทำอย่างไรเมื่อผลลัพธ์ไม่พอดีกับ int ฉันคิดว่าการแคสติ้งโอเค แต่ก็ยังมี gotcha อยู่บ้าง:

  1. สำหรับ enums ตามประเภทที่มีช่องว่างขนาดใหญ่กว่า int (long และ ulong) เป็นไปได้ว่า enums บางตัวจะประเมินเป็นค่าเดียวกัน
  2. การร่ายตัวเลขที่มากกว่า int MaxValue จะทำให้เกิดข้อยกเว้นหากคุณอยู่ในพื้นที่ที่เลือก

นี่คือคำแนะนำของฉันฉันจะปล่อยให้ผู้อ่านตัดสินใจว่าจะเปิดฟังก์ชั่นนี้ที่ไหน เป็นผู้ช่วยหรือส่วนขยาย

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

-1

อย่าลืมว่าประเภท Enum เองก็มีฟังก์ชันตัวช่วยแบบคงที่อยู่มากมาย หากสิ่งที่คุณต้องการทำคือแปลงอินสแตนซ์ของ enum เป็นประเภทจำนวนเต็มที่สอดคล้องกันการแคสต์น่าจะเป็นวิธีที่มีประสิทธิภาพมากที่สุด

ฉันคิดว่า ReSharper กำลังบ่นเพราะ Enum ไม่ใช่การแจงนับประเภทใดประเภทหนึ่งและการแจงนับเองมาจากค่าสเกลาร์ไม่ใช่ Enum หากคุณต้องการการหล่อแบบปรับเปลี่ยนได้ด้วยวิธีทั่วไปฉันจะบอกว่าสิ่งนี้สามารถตอบโจทย์คุณได้ดี (โปรดทราบว่าประเภทการแจงนับนั้นรวมอยู่ในประเภททั่วไปด้วย:

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

สิ่งนี้สามารถใช้ได้ดังนี้:

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

// ...

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

ฉันไม่สามารถพูดออกมาจากด้านบนของหัวได้อย่างแน่นอน แต่การอนุมานประเภท C # อาจรองรับโค้ดด้านบนด้วยซึ่งอนุญาตสิ่งต่อไปนี้:

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

1
ขออภัยในตัวอย่างของคุณ E สามารถเป็นประเภทใดก็ได้ Generics ไม่อนุญาตให้ฉันใช้ Enum เป็นข้อ จำกัด ของพารามิเตอร์ประเภท ฉันไม่สามารถพูดได้: โดยที่ T: Enum
Vadim

คุณสามารถใช้โดยที่ T: struct ได้ มันไม่ค่อยเข้มงวดเท่าที่คุณต้องการ แต่มันจะตัดประเภทการอ้างอิงใด ๆ ออกไป (ซึ่งฉันคิดว่าเป็นสิ่งที่คุณพยายามจะทำ)
jrista

คุณสามารถใช้: if (! typeof (E) .IsEnum) โยน ArgumentException ใหม่ ("E ต้องเป็นประเภทที่แจกแจง");
diadiora

1
สิ่งนี้ไม่ถูกต้องจากระยะไกลตัวช่วยแบบคงที่ยังไม่ถูกต้อง
Nicholi

1
ทำไมต้องใช้คนอื่นEnumHelpers.Convert<int, StopLight>(StopLight.Red);แทน(int)StopLight.Read;? System.Enumนอกจากนี้ยังเป็นคำถามที่พูดคุยเกี่ยวกับ
nawfal
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.