Como adicionar métodos de extensão ao Enums

122

Eu tenho este código Enum:

enum Duration { Day, Week, Month };

Posso adicionar métodos de extensão para este Enum?

user2110292
fonte
1
você viu isso? stackoverflow.com/questions/2422113/…
badpanda
1
Além disso, isso. stackoverflow.com/questions/276585/…
badpanda 13/03
resposta curta, sim. Neste caso específico, você pode querer considerar o uso deTimeSpan
Jodrell
1
Usar métodos de extensão em uma enumeração me faria sentir suja. Crie uma classe para encapsular o que é necessário. Mantenha uma enumeração o mais simples possível. Se você precisar de mais lógica associada, crie uma classe Duration que exponha dia, semana, mês e mais qualquer outra lógica que estivesse no método de extensão.
Jason Evans
2
Eu gosto de ter métodos de extensão de enumeração para grupos de sinalizadores. Eu prefiro em se cláusulas por exemplo Day.IsWorkday()sobre (Day & Days.Workday) > 0com Days.Workdaydefinido como Monday | Tuesday ... | Friday. O primeiro é mais claro na minha opinião e implementou exatamente o último.
Sebastian Werk

Respostas:

114

De acordo com este site :

Os métodos de extensão fornecem uma maneira de escrever métodos para as classes existentes de uma maneira que outras pessoas da sua equipe possam descobrir e usar. Dado que enums são classes como qualquer outra, não deve ser muito surpreendente que você possa estendê-las, como:

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

Eu acho que enums não são a melhor escolha em geral, mas pelo menos isso permite centralizar parte do switch / se manipular e abstraí-los um pouco até que você possa fazer algo melhor. Lembre-se de verificar se os valores também estão no intervalo.

Você pode ler mais aqui no Microsft MSDN.

Tripulação de um homem
fonte
Acredito que o comentário "enums é mau" está fora de lugar, mas tem uma base na realidade. Eu acho que as enums podem ser um problema que é usado em excesso, pois elas meio que prendem você em certos contextos e comportamentos.
Ed Schwehm
1
Enums em C # tipo de chupar porque este compila e roda:Duration d = 0;
Graham
16
Given that enums are classesnão, eles não são classes.
Winger Sendon
1
Eu uso esse tipo de extensão apenas se for sobre o enum diretamente, como dias da semana e métodos de extensão IsWeekday (), IsWeekendday (). Mas as classes têm o objetivo de encapsular comportamentos; portanto, se há muitos ou comportamentos complicados para encapsular, uma classe provavelmente é melhor. Se é limitado e básico, eu pessoalmente acho que extensões em enumerações são boas. Como na maioria das decisões de design, existem limites difusos entre escolhas (IMO). Vale ressaltar que as extensões só podem ser feitas nas classes estáticas de nível superior, e não nas classes aninhadas. Se sua enumeração faz parte de uma classe, você precisará fazer uma aula.
FreeText
51

Você também pode adicionar um método de extensão ao tipo Enum, em vez de uma instância do 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;
        }
    }
}

Você pode invocar o método de extensão acima fazendo:

var result = Enum<Duration>.Count;

Não é um verdadeiro método de extensão. Funciona apenas porque Enum <> é um tipo diferente de System.Enum.

ShawnFeatherly
fonte
A classe staticpode garantir que todos os seus métodos se comportem como extensões?
Jatin Sanghvi
15
Para futuros leitores: a ambiguidade do nome Enum<T>é um pouco confusa. A classe também pode ser chamada EnumUtils<T>e a chamada do método seria resolvida EnumUtils<Duration>.Count.
Namoshek 27/02/19
46

Claro que você pode, digamos, por exemplo, que você queira usar o valor DescriptionAttribueem seus enumvalores:

using System.ComponentModel.DataAnnotations;

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

    [Description("Five days")]
    Week,

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

Agora você deseja poder fazer algo como:

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

Seu método de extensão GetDescription()pode ser escrito da seguinte maneira:

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;
}
Empilhados
fonte
Eu estava olhando para criar uma extensão para quase exatamente o que você fez, exceto o meu uso DisplayAttribute Localized GetDescription. Cheers
George
Essa é uma boa alternativa, embora eu ache que o espaço para nome seja apenas System.ComponentModel?
TomDestry
Boa perspectiva e obrigado por mostrar a implementação e o código de extensão. Para acumular: com sua implementação, você também pode chamá-lo assim: var description = Duration.Week.GetDescription ();
Spencer Sullivan
30

Todas as respostas são ótimas, mas elas estão falando sobre adicionar um método de extensão a um tipo específico de enumeração.

E se você quiser adicionar um método a todas as enumerações, como retornar um int do valor atual, em vez da conversão explícita?

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

O truque por trás IConvertibleé sua hierarquia de herança, veja MDSN

Obrigado a ShawnFeatherly por sua resposta

Basheer AL-MOMANI
fonte
1
a melhor resposta!
Marco Alves
Idem. Ele funciona se eu chamar a extensão diretamente (por exemplo MyExtention.DoThing(myvalue)), mas na verdade não atribuem à enumeração (por exemplo myvalue.DoThing())
Sinaesthetic
1
FYI, C # 7.3 agora suporta Enum como um tipo de restrição genérica
redtetrahedron
7

Você pode criar uma extensão para qualquer coisa, mesmo object(embora isso não seja considerado uma prática recomendada ). Entenda um método de extensão como um public staticmétodo. Você pode usar qualquer tipo de parâmetro que desejar nos métodos.

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

Veja MSDN .

public static class Extensions
{
  public static string SomeMethod(this Duration enumValue)
  {
    //Do something here
    return enumValue.ToString("D"); 
  }
}
LukeHennerley
fonte
7
Um voidvalor de retorno em uma enumeração é meio estranho. Eu pensaria em uma amostra mais realista.
psubsee2003
3
@ psubsee2003 o OP certamente tem conhecimento suficiente para mudar isso para atender às suas necessidades? Por que a amostra importa, basta responder à pergunta inicial.
LukeHennerley
3
Eu sou o único que acha os exemplos de código no MSDN estranhos? Na maioria das vezes, você precisa de algum esforço real para entender o que eles estão tentando fazer!
Empilhado
0

acabamos de fazer uma extensão de enum para c # https://github.com/simonmau/enum_ext

É apenas uma implementação para o typesafeenum, mas funciona muito bem, então criamos um pacote para compartilhar - divirta-se com ele

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

Eu sei que o exemplo é meio inútil, mas você entendeu;)

simonmau
fonte
0

Uma solução simples.

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

        return ( int ) ( IConvertible ) payLoad;

    }
}

int num = YourEnum.AItem.ToInt();
Console.WriteLine("num : ", num);
M. Hamza Rajput
fonte