วิธีเพิ่มวิธีการขยายใน Enums


122

ฉันมีรหัส Enum นี้:

enum Duration { Day, Week, Month };

ฉันสามารถเพิ่มวิธีการขยายสำหรับ Enum นี้ได้หรือไม่?


1
คุณเคยเห็นสิ่งนี้หรือไม่? stackoverflow.com/questions/2422113/…
badpanda

1
นอกจากนี้ stackoverflow.com/questions/276585/…
badpanda

คำตอบสั้น ๆ ใช่ ในกรณีเฉพาะนี้คุณอาจต้องการพิจารณาใช้TimeSpan
Jodrell

1
การใช้วิธีการต่อกับ enum จะทำให้ฉันรู้สึกสกปรก สร้างคลาสเพื่อห่อหุ้มสิ่งที่จำเป็น ทำให้ enum ง่ายที่สุด หากคุณต้องการตรรกะเพิ่มเติมที่เกี่ยวข้องให้สร้างคลาส Duration ที่แสดงวันสัปดาห์เดือนบวกด้วยตรรกะอื่น ๆ ที่น่าจะอยู่ในเมธอดส่วนขยาย
Jason Evans

2
ฉันชอบมีวิธีการขยาย enum สำหรับกลุ่มแฟล็ก ฉันชอบใน if clauses เช่นDay.IsWorkday()มากกว่า(Day & Days.Workday) > 0ด้วยDays.Workdayกำหนดเป็นMonday | Tuesday ... | Friday. อดีตมีความชัดเจนมากขึ้นในความคิดของฉันและมีการนำไปใช้อย่างแน่นอน
Sebastian Werk

คำตอบ:


114

ตามเว็บไซต์นี้:

วิธีการขยายเป็นวิธีการเขียนวิธีการสำหรับชั้นเรียนที่มีอยู่ในแบบที่คนอื่น ๆ ในทีมของคุณอาจค้นพบและนำไปใช้ได้จริง เนื่องจาก enums เป็นคลาสอื่น ๆ จึงไม่น่าแปลกใจที่คุณสามารถขยายได้เช่น:

enum Duration { Day, Week, Month };

static class DurationExtensions 
{
  public static DateTime From(this Duration duration, DateTime dateTime) 
  {
    switch (duration) 
    {
      case Day:   return dateTime.AddDays(1);
      case Week:  return dateTime.AddDays(7);
      case Month: return dateTime.AddMonths(1);
      default:    throw new ArgumentOutOfRangeException("duration");
    }
  }
}

ฉันคิดว่า enums ไม่ใช่ตัวเลือกที่ดีที่สุดโดยทั่วไป แต่อย่างน้อยก็ช่วยให้คุณสามารถรวมสวิตช์บางส่วน / หากจัดการและแยกพวกเขาออกไปเล็กน้อยจนกว่าคุณจะสามารถทำสิ่งที่ดีกว่าได้ อย่าลืมตรวจสอบค่าที่อยู่ในช่วงด้วย

คุณสามารถอ่านเพิ่มเติมได้ที่นี่ที่ Microsft MSDN


ฉันเชื่อว่าความคิดเห็น "enums are evil" ไม่อยู่ในสถานที่ แต่มีพื้นฐานในความเป็นจริง ฉันพบว่า enums อาจเป็นปัญหามากเกินไปเนื่องจากพวกเขาล็อคคุณไว้ในบริบทและพฤติกรรมบางอย่าง
Ed Schwehm

1
Enums ใน C # ดูดเพราะสิ่งนี้รวบรวมและดำเนินการ:Duration d = 0;
Graham

16
Given that enums are classesไม่ใช่พวกเขาไม่ใช่ชั้นเรียน
Winger Sendon

1
ฉันใช้ส่วนขยายประเภทนี้เฉพาะในกรณีที่เกี่ยวกับ enum โดยตรงเช่นวันในสัปดาห์และวิธีการขยาย IsWeekday (), IsWeekendday () แต่คลาสมีไว้เพื่อห่อหุ้มพฤติกรรมดังนั้นหากมีพฤติกรรมที่ซับซ้อนหรือซับซ้อนในการห่อหุ้มชั้นเรียนก็น่าจะดีกว่า หากมีข้อ จำกัด และเป็นพื้นฐานฉันพบว่าส่วนขยายใน enums โอเค เช่นเดียวกับการตัดสินใจในการออกแบบส่วนใหญ่มีขอบเขตที่คลุมเครือระหว่างตัวเลือก (IMO) เป็นที่น่าสังเกตว่าส่วนขยายสามารถทำได้ในคลาสคงที่ระดับบนสุดเท่านั้นไม่ใช่คลาสที่ซ้อนกัน หาก enum ของคุณเป็นส่วนหนึ่งของชั้นเรียนคุณจะต้องสร้างชั้นเรียน
FreeText

51

คุณยังสามารถเพิ่มวิธีการขยายให้กับประเภท Enum แทนที่จะเป็นอินสแตนซ์ของ Enum:

/// <summary> Enum Extension Methods </summary>
/// <typeparam name="T"> type of Enum </typeparam>
public class Enum<T> where T : struct, IConvertible
{
    public static int Count
    {
        get
        {
            if (!typeof(T).IsEnum)
                throw new ArgumentException("T must be an enumerated type");

            return Enum.GetNames(typeof(T)).Length;
        }
    }
}

คุณสามารถเรียกใช้วิธีการขยายด้านบนโดยทำ:

var result = Enum<Duration>.Count;

ไม่ใช่วิธีการขยายที่แท้จริง ใช้ได้เฉพาะเนื่องจาก Enum <> เป็นประเภทที่แตกต่างจาก System.Enum


ชั้นเรียนสามารถstaticตรวจสอบให้แน่ใจว่าวิธีการทั้งหมดทำงานเหมือนส่วนขยายหรือไม่
Jatin Sanghvi

15
สำหรับผู้อ่านในอนาคต: ความคลุมเครือของชื่อEnum<T>ค่อนข้างสับสน นอกจากนี้ยังสามารถเรียกคลาสได้EnumUtils<T>และการเรียกเมธอดจะแก้ไขEnumUtils<Duration>.Countได้
Namoshek

46

แน่นอนคุณสามารถพูดได้เช่นคุณต้องการใช้ค่าDescriptionAttribueของคุณenum:

using System.ComponentModel.DataAnnotations;

public enum Duration 
{ 
    [Description("Eight hours")]
    Day,

    [Description("Five days")]
    Week,

    [Description("Twenty-one days")] 
    Month 
}

ตอนนี้คุณต้องการที่จะทำสิ่งต่างๆเช่น:

Duration duration = Duration.Week;
var description = duration.GetDescription(); // will return "Five days"

วิธีการขยายของคุณGetDescription()สามารถเขียนได้ดังนี้:

using System.ComponentModel;
using System.Reflection;

public static string GetDescription(this Enum value)
{
    FieldInfo fieldInfo = value.GetType().GetField(value.ToString());
    if (fieldInfo == null) return null;
    var attribute = (DescriptionAttribute)fieldInfo.GetCustomAttribute(typeof(DescriptionAttribute));
    return attribute.Description;
}

ฉันต้องการสร้างส่วนขยายให้เกือบทุกอย่างที่คุณทำตัวอย่างยกเว้นการใช้ DisplayAttribute Localized GetDescription ไชโย
จอร์จ

นี่เป็นทางเลือกที่ดีแม้ว่าฉันคิดว่าเนมสเปซเป็นเพียง System.ComponentModel?
TomDestry

มุมมองที่ดีและขอขอบคุณที่แสดงการใช้งานรวมถึงโค้ดส่วนขยาย ในการกอง: ด้วยการใช้งานของคุณคุณสามารถเรียกสิ่งนี้ได้เช่นนี้: var description = Duration.Week.GetDescription ();
Spencer Sullivan

32

คำตอบทั้งหมดนั้นดีมาก แต่พวกเขากำลังพูดถึงการเพิ่มวิธีการขยายลงใน enum ประเภทเฉพาะ

จะเกิดอะไรขึ้นถ้าคุณต้องการเพิ่มวิธีการให้กับ enums ทั้งหมดเช่นส่งกลับค่า int ของค่าปัจจุบันแทนการแคสต์อย่างชัดเจน?

public static class EnumExtensions
{
    public static int ToInt<T>(this T soure) where T : IConvertible//enum
    {
        if (!typeof(T).IsEnum)
            throw new ArgumentException("T must be an enumerated type");

        return (int) (IConvertible) soure;
    }

    //ShawnFeatherly funtion (above answer) but as extention method
    public static int Count<T>(this T soure) where T : IConvertible//enum
    {
        if (!typeof(T).IsEnum)
            throw new ArgumentException("T must be an enumerated type");

        return Enum.GetNames(typeof(T)).Length;
    }
}

เคล็ดลับเบื้องหลังIConvertibleคือลำดับชั้นการสืบทอดดูMDSN

ขอบคุณShawnFeatherly สำหรับคำตอบของเขา


1
คำตอบที่ดีที่สุด!
Marco Alves

เหมือนกัน ใช้งานได้ถ้าฉันเรียกส่วนขยายโดยตรง (เช่นMyExtention.DoThing(myvalue)) แต่ไม่ได้แนบกับ enum (เช่นmyvalue.DoThing())
Sinaesthetic

1
FYI, C # 7.3 รองรับ Enum เป็นข้อ จำกัด ประเภททั่วไปแล้ว
redtetrahedron

7

คุณสามารถสร้างส่วนขยายสำหรับอะไรก็ได้object(แม้ว่าจะไม่ถือเป็นแนวทางปฏิบัติที่ดีที่สุดก็ตาม) ทำความเข้าใจวิธีการขยายเช่นเดียวกับpublic staticวิธีการ คุณสามารถใช้พารามิเตอร์ประเภทใดก็ได้ที่คุณต้องการในวิธีการ

public static class DurationExtensions
{
  public static int CalculateDistanceBetween(this Duration first, Duration last)
  {
    //Do something here
  }
}

5

ดูMSDN

public static class Extensions
{
  public static string SomeMethod(this Duration enumValue)
  {
    //Do something here
    return enumValue.ToString("D"); 
  }
}

7
voidค่าตอบแทนใน enum เป็นชนิดของแปลก ฉันจะนึกถึงตัวอย่างที่เป็นจริงมากกว่านี้
psubsee2003

3
@ psubsee2003 OP มีความรู้เพียงพอที่จะเปลี่ยนสิ่งนี้ให้เหมาะกับความต้องการของเขาหรือไม่? เหตุใดตัวอย่างจึงมีความสำคัญก็เพียงพอที่จะตอบคำถามเริ่มต้น
LukeHennerley

3
ฉันเป็นคนเดียวที่พบตัวอย่างโค้ดบน MSDN แปลก ๆ หรือไม่? ส่วนใหญ่แล้วคุณต้องใช้ความพยายามอย่างแท้จริงเพื่อทำความเข้าใจว่าพวกเขากำลังพยายามทำอะไรอยู่!
ซ้อน

0

เราเพิ่งสร้างส่วนขยาย enum สำหรับ c # https://github.com/simonmau/enum_ext

มันเป็นเพียงการใช้งานสำหรับ typesafeenum แต่มันใช้งานได้ดีดังนั้นเราจึงสร้างแพ็คเกจเพื่อแบ่งปัน - ขอให้สนุกกับมัน

public sealed class Weekday : TypeSafeNameEnum<Weekday, int>
{
    public static readonly Weekday Monday = new Weekday(1, "--Monday--");
    public static readonly Weekday Tuesday = new Weekday(2, "--Tuesday--");
    public static readonly Weekday Wednesday = new Weekday(3, "--Wednesday--");
    ....

    private Weekday(int id, string name) : base(id, name)
    {
    }

    public string AppendName(string input)
    {
        return $"{Name} {input}";
    }
}

ฉันรู้ว่าตัวอย่างนั้นไร้ประโยชน์ แต่คุณได้รับความคิด;)


0

วิธีแก้ปัญหาง่ายๆ

public static class EnumExtensions
{
    public static int ToInt(this Enum payLoad) {

        return ( int ) ( IConvertible ) payLoad;

    }
}

int num = YourEnum.AItem.ToInt();
Console.WriteLine("num : ", num);
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.