Как добавить методы расширения в Enums

122

У меня есть этот код Enum:

enum Duration { Day, Week, Month };

Могу ли я добавить методы расширения для этого Enum?

user2110292
источник
1
Вы видели это? stackoverflow.com/questions/2422113/…
badpanda
1
Кроме того, это. stackoverflow.com/questions/276585/…
badpanda
короткий ответ, да. В этом конкретном случае вы можете рассмотреть возможность использованияTimeSpan
Джодрелл
1
Использование методов расширения в перечислении заставит меня почувствовать себя грязным. Создайте класс для инкапсуляции того, что необходимо. Сделайте перечисление как можно более простым. Если вам нужна дополнительная логика, связанная с ним, создайте класс Duration, который предоставляет день, неделю, месяц, а также содержит любую другую логику, которая была бы в методе расширения.
Джейсон Эванс,
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");
    }
  }
}

Я думаю, что перечисления - не лучший выбор в целом, но, по крайней мере, это позволяет вам централизовать часть обработки switch / if и немного их абстрагировать, пока вы не сможете сделать что-то лучше. Не забудьте также проверить, что значения находятся в диапазоне.

Вы можете прочитать больше здесь, на Microsft MSDN.

Один человек
источник
Я считаю, что комментарий «перечисления - зло» неуместен, но имеет реальную основу. Я действительно считаю, что перечисления могут быть проблемой, поскольку они как бы привязывают вас к определенным контекстам и поведению.
Эд Швем
1
Перечисления в C # - отстой, потому что он компилируется и запускается:Duration d = 0;
Грэм,
16
Given that enums are classesнет, это не классы.
Winger
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.

ShawnFeatherly
источник
Может ли класс 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;
}
Stacked
источник
Я искал расширение почти точно для того, что делал ваш образец, за исключением того, что я использовал DisplayAttribute Localized GetDescription. ура
Джордж
Это хорошая альтернатива, хотя я думаю, что пространство имен - это просто System.ComponentModel?
TomDestry
Хорошая перспектива и спасибо за то, что показали реализацию, а также код расширения. Вкратце: в своей реализации вы также можете называть ее так: var description = Duration.Week.GetDescription ();
Спенсер Салливан,
32

Все ответы хороши, но они говорят о добавлении метода расширения к определенному типу перечисления.

Что, если вы хотите добавить метод ко всем перечислениям, например, вернуть 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
лучший ответ!
Марко Алвес
То же самое. Он работает, если я вызываю расширение напрямую (например MyExtention.DoThing(myvalue)), но на самом деле не присоединяюсь к перечислению (например myvalue.DoThing())
Sinaesthetic 05
1
К вашему сведению, 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"); 
  }
}
LukeHennerley
источник
7
voidВозвращаемое значение на перечисление является своего рода странно. Я бы подумал о более реалистичном образце.
psubsee2003
3
@ psubsee2003 у ОП наверняка достаточно знаний, чтобы изменить это в соответствии со своими потребностями? Почему образец имеет значение, этого достаточно, чтобы ответить на первоначальный вопрос.
Люк Хеннерли
3
Я единственный, кто считает примеры кода в MSDN странными? В большинстве случаев вам нужны реальные усилия, чтобы понять, что они пытаются делать!
сложено
0

мы только что сделали расширение enum для C # https://github.com/simonmau/enum_ext

Это просто реализация typeafeenum, но она отлично работает, поэтому мы сделали пакет, которым можно поделиться - получайте удовольствие

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

Я знаю, что этот пример бесполезен, но идею вы поняли;)

simonmau
источник
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);
М. Хамза Раджпут
источник