Cách thêm các phương thức mở rộng vào Enums


122

Tôi có mã Enum này:

enum Duration { Day, Week, Month };

Tôi có thể thêm các phương thức mở rộng cho Enum này không?


1
Bạn đã thấy cái này chưa? stackoverflow.com/questions/2422113/…
badpanda

1

câu trả lời ngắn gọn, có. Trong trường hợp cụ thể này, bạn có thể muốn xem xét việc sử dụngTimeSpan
Jodrell

1
Sử dụng các phương thức mở rộng trên một enum sẽ khiến tôi cảm thấy bẩn. Tạo một lớp để đóng gói những gì cần thiết. Giữ một enum càng đơn giản càng tốt. Nếu bạn cần thêm logic liên quan đến nó, thì hãy tạo một lớp Duration hiển thị ngày, tuần, tháng cộng với bất kỳ logic nào khác có trong phương thức mở rộng.
Jason Evans

2
Tôi thích có các phương thức mở rộng enum cho các nhóm cờ. Tôi thích các mệnh đề if Day.IsWorkday()hơn (Day & Days.Workday) > 0với Days.Workdayđược định nghĩa là Monday | Tuesday ... | Friday. Cái đầu tiên rõ ràng hơn theo ý kiến ​​của tôi và đã thực hiện chính xác cái sau.
Sebastian Werk

Câu trả lời:


114

Theo trang web này :

Các phương thức mở rộng cung cấp cách viết phương thức cho các lớp hiện có theo cách mà những người khác trong nhóm của bạn có thể thực sự khám phá và sử dụng. Cho rằng enums là các lớp giống như bất kỳ lớp nào khác, không quá ngạc nhiên khi bạn có thể mở rộng chúng, như:

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

Tôi nghĩ rằng enums không phải là lựa chọn tốt nhất nói chung nhưng ít nhất điều này cho phép bạn tập trung một số chuyển đổi / nếu xử lý và trừu tượng hóa chúng đi một chút cho đến khi bạn có thể làm điều gì đó tốt hơn. Hãy nhớ kiểm tra các giá trị cũng nằm trong phạm vi.

Bạn có thể đọc thêm tại đây tại Microsft MSDN.


Tôi tin nhận xét "enums is evil" là lạc lõng nhưng có cơ sở trên thực tế. Tôi thấy rằng enums có thể là một vấn đề được sử dụng quá mức, vì chúng sắp xếp bạn vào một số bối cảnh và hành vi nhất định.
Ed Schwehm

1
Enums trong C # thật hấp dẫn vì nó biên dịch và chạy:Duration d = 0;
Graham.

16
Given that enums are classeskhông, họ không phải là lớp học.
Winger Sendon

1
Tôi chỉ sử dụng loại tiện ích mở rộng này nếu nó nói về enum trực tiếp, như các ngày trong tuần và các phương thức mở rộng IsWeekday (), IsWeekendday (). Nhưng các lớp có nghĩa là để đóng gói các hành vi, vì vậy nếu có nhiều hành vi hoặc phức tạp để đóng gói, một lớp có lẽ tốt hơn. Nếu nó bị hạn chế và cơ bản, cá nhân tôi thấy các tiện ích mở rộng trên enums vẫn ổn. Như với hầu hết các quyết định thiết kế, có ranh giới mờ giữa các lựa chọn (IMO). Cần lưu ý rằng phần mở rộng chỉ có thể được thực hiện trên các lớp tĩnh cấp cao nhất, không phải các lớp lồng nhau. Nếu enum của bạn là một phần của một lớp, bạn sẽ cần tạo một lớp.
FreeText

51

Bạn cũng có thể thêm một phương thức mở rộng vào kiểu Enum thay vì một phiên bản của 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;
        }
    }
}

Bạn có thể gọi phương thức mở rộng ở trên bằng cách thực hiện:

var result = Enum<Duration>.Count;

Nó không phải là một phương pháp mở rộng thực sự. Nó chỉ hoạt động vì Enum <> là một kiểu khác với System.Enum.


Lớp có thể staticđảm bảo tất cả các phương thức của nó hoạt động giống như phần mở rộng không?
Jatin Sanghvi

15
Đối với độc giả trong tương lai: sự không rõ ràng về tên của Enum<T>là một chút khó hiểu. Lớp cũng có thể được gọi EnumUtils<T>và cuộc gọi phương thức sẽ giải quyết thành EnumUtils<Duration>.Count.
Namoshek

46

Tất nhiên, bạn có thể, chẳng hạn, bạn muốn sử dụng các giá trị DescriptionAttribuecủa mình enum:

using System.ComponentModel.DataAnnotations;

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

    [Description("Five days")]
    Week,

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

Bây giờ bạn muốn có thể làm điều gì đó như:

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

Phương thức mở rộng của bạn GetDescription()có thể được viết như sau:

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

tôi đang tìm cách tạo tiện ích mở rộng gần như chính xác những gì bạn lấy mẫu, ngoại trừ việc tôi sử dụng DisplayAttribute Localized GetDescription. cổ vũ
George

Đây là một sự thay thế tốt, mặc dù tôi nghĩ rằng không gian tên chỉ là System.ComponentModel?
TomDestry

Phối cảnh đẹp và cảm ơn bạn đã hiển thị cách triển khai cũng như mã mở rộng. Để bổ sung: với cách triển khai của bạn, bạn cũng có thể gọi nó như thế này: var description = Duration.Week.GetDescription ();
Spencer Sullivan

31

Tất cả các câu trả lời đều tuyệt vời, nhưng họ đang nói về việc thêm phương thức mở rộng vào một loại enum cụ thể.

Điều gì sẽ xảy ra nếu bạn muốn thêm một phương thức vào tất cả các enum như trả về giá trị int của giá trị hiện tại thay vì ép kiểu rõ ràng?

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

Bí quyết đằng sau IConvertiblelà Hệ thống phân cấp thừa kế của nó, hãy xem MDSN

Cảm ơn ShawnFeatherly vì câu trả lời của anh ấy


1
câu trả lời tốt nhất!
Marco Alves

Như trên. Nó hoạt động nếu tôi gọi trực tiếp tiện ích mở rộng (ví dụ MyExtention.DoThing(myvalue)) nhưng không thực sự đính kèm với enum (ví dụ myvalue.DoThing():)
Sinaesthetic

1
FYI, C # 7.3 hiện hỗ trợ Enum dưới dạng ràng buộc kiểu chung
redtetrahedron

7

Bạn có thể tạo tiện ích mở rộng cho bất kỳ thứ gì, ngay cả object(mặc dù điều đó không được coi là phương pháp hay nhất ). Hiểu một phương thức mở rộng chỉ như một public staticphương pháp. Bạn có thể sử dụng bất kỳ kiểu tham số nào bạn thích trên các phương thức.

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

5

Xem MSDN .

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

7
Một voidgiá trị lợi nhuận trên một enum là loại kỳ lạ. Tôi muốn nghĩ về một mẫu thực tế hơn.
psubsee2003

3
@ psubsee2003 OP chắc chắn có đủ kiến ​​thức để thay đổi điều này cho phù hợp với nhu cầu của mình? Tại sao mẫu quan trọng, nó đủ để trả lời câu hỏi ban đầu.
LukeHennerley

3
Tôi có phải là người duy nhất thấy các ví dụ mã trên MSDN kỳ lạ không? Hầu hết thời gian bạn cần nỗ lực thực sự để hiểu những gì họ đang cố gắng làm!
Xếp chồng

0

chúng tôi vừa tạo một tiện ích mở rộng enum cho c # https://github.com/simonmau/enum_ext

Nó chỉ là một triển khai cho typeafeenum, nhưng nó hoạt động rất tốt, vì vậy chúng tôi đã tạo một gói để chia sẻ - hãy vui vẻ với nó

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

Tôi biết ví dụ này hơi vô ích, nhưng bạn hiểu rồi đấy;)


0

Một cách giải quyết đơn giản.

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

        return ( int ) ( IConvertible ) payLoad;

    }
}

int num = YourEnum.AItem.ToInt();
Console.WriteLine("num : ", num);
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.