Як додати методи розширення до Enums


122

У мене є цей код Enum:

enum Duration { Day, Week, Month };

Чи можна додати методи розширення для цього Enum?




коротка відповідь, так. У цьому конкретному випадку ви можете розглянути питання про використанняTimeSpan
Jodrell

1
Використання методів розширення на перерахунок змусить мене почуватися брудним. Створіть клас, щоб інкапсулювати те, що потрібно. Зберігайте перерахунок якомога простіше. Якщо вам потрібно більше логіки, пов'язаної з нею, тоді створіть клас "Тривалість", який відображає день, тиждень, місяць плюс містить будь-яку іншу логіку, яка була б у методі розширення.
Джейсон Еванс

2
Мені подобається, що методи розширення enum для груп прапорів. Я вважаю за краще, якщо застереження наприклад Day.IsWorkday()через (Day & Days.Workday) > 0з Days.Workdayвизначається як Monday | Tuesday ... | Friday. Перший є більш чітким, на мою думку, і саме останній реалізований.
Себастьян Верк

Відповіді:


114

За даними цього сайту :

Методи розширення - це спосіб написати методи для існуючих класів таким чином, щоб інші люди у вашій команді могли насправді виявити та використовувати. Зважаючи на те, що перерахунки - це класи, як і будь-які інші, не повинно надто дивно, що ви можете продовжити їх, наприклад:

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

Я думаю, що перерахунки взагалі не найкращий вибір, але, принаймні, це дозволяє вам централізувати деякі перемикачі / якщо керувати ними та трохи абстрагувати їх, поки ви не зможете зробити щось краще. Не забудьте перевірити, чи знаходяться також значення в діапазоні.

Детальніше ви можете прочитати тут на Microsft MSDN.


Я вважаю, що коментар "переслідувачі є злими" не має місця, але в реальності має основу. Я вважаю, що перелякування може бути проблемою, надмірне використання, оскільки вони ніби замикають вас у певних контекстах і поведінці.
Ed Schwehm

1
Перераховує на C # різновид смоктання, оскільки це збирає та працює:Duration d = 0;
Грем

16
Given that enums are classesні, вони не є заняттями.
Вінгер Сендон

1
Я використовую таке розширення лише в тому випадку, якщо мова йде про перерахування безпосередньо, як дні тижня та методи розширення IsWeekday (), IsWeekendday (). Але класи призначені для інкапсуляції поведінки, тому, якщо інкапсуляція буде багато або складна, то, ймовірно, краще. Якщо він обмежений і базовий, я особисто знаходжу розширення на перерахунки добре. Як і у більшості дизайнерських рішень, існують нечіткі межі між варіантами (IMO). Варто зазначити, що розширення можна робити лише на статичних класах верхнього рівня, а не на вкладених класах. Якщо ваш перелік є частиною класу, вам потрібно скласти клас.
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забезпечити, щоб усі його методи поводилися як розширення?
Джатін Сангві

15
Для майбутніх читачів: неоднозначність імені Enum<T>трохи заплутано. Клас також може бути викликаний, EnumUtils<T>і виклик методу вирішиться до EnumUtils<Duration>.Count.
Намошек

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 ();
Спенсер Салліван

31

Усі відповіді чудові, але вони говорять про додавання методу розширення до певного типу перерахунків.

Що робити, якщо ви хочете додати метод до всіх перерахунків, як повернення 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

Дякуємо ШонФеверлі за його відповідь


1
найкраща відповідь!
Марко Алвеш

Дітто. Це працює, якщо я дзвоню розширення безпосередньо (наприклад 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Значення, що повертається на перерахування є свого роду дивно. Я б подумав про більш реалістичний зразок.
psubsee2003

3
@ psubsee2003 ОП напевно має достатньо знань, щоб змінити це відповідно до його потреб? Чому вибірка має значення, досить відповісти на початкове запитання.
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);
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.