Cómo agregar métodos de extensión a Enums

122

Tengo este código Enum:

enum Duration { Day, Week, Month };

¿Puedo agregar métodos de extensión para esta enumeración?

usuario2110292
fuente
1
¿Has visto esto? stackoverflow.com/questions/2422113/…
badpanda
1
También esto. stackoverflow.com/questions/276585/…
badpanda
respuesta corta, sí. En este caso específico, es posible que desee considerar el uso deTimeSpan
Jodrell
1
Usar métodos de extensión en una enumeración me haría sentir sucio. Crea una clase para encapsular lo que se necesita. Mantenga una enumeración lo más simple posible. Si necesita más lógica asociada con él, cree una clase de Duración que exponga el día, la semana, el mes y contenga cualquier otra lógica que hubiera estado en el método de extensión.
Jason Evans
2
Me gusta tener métodos de extensión de enumeración para grupos de banderas. Prefiero las cláusulas if, por ejemplo, Day.IsWorkday()en lugar (Day & Days.Workday) > 0de Days.Workdaydefinidas como Monday | Tuesday ... | Friday. El primero es más claro en mi opinión y tiene exactamente el segundo implementado.
Sebastian Werk

Respuestas:

114

Según este sitio :

Los métodos de extensión proporcionan una forma de escribir métodos para clases existentes de una manera que otras personas de su equipo podrían descubrir y utilizar. Dado que las enumeraciones son clases como cualquier otra, no debería ser demasiado sorprendente que pueda extenderlas, 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");
    }
  }
}

Creo que las enumeraciones no son la mejor opción en general, pero al menos esto te permite centralizar algunos de los cambios / if manipularlos y abstraerlos un poco hasta que puedas hacer algo mejor. Recuerde verificar que los valores también estén dentro del rango.

Puede leer más aquí en Microsft MSDN.

Tripulación de un hombre
fuente
Creo que el comentario de "las enumeraciones son malas" está fuera de lugar pero tiene una base en la realidad. Encuentro que las enumeraciones pueden ser un problema si se usan en exceso, ya que de alguna manera te encierran en ciertos contextos y comportamientos.
Ed Schwehm
1
Enumeraciones en C # tipo de chupar, porque esto puede compilar y ejecutar:Duration d = 0;
Graham
16
Given that enums are classesno, no son clases.
Winger Sendon
1
Utilizo este tipo de extensión solo si se trata directamente de la enumeración, como los días de la semana y los métodos de extensión IsWeekday (), IsWeekendday (). Pero las clases están destinadas a encapsular comportamientos, por lo que si hay muchos comportamientos complicados que encapsular, una clase probablemente sea mejor. Si es limitado y básico, personalmente encuentro bien las extensiones en enumeraciones. Como ocurre con la mayoría de las decisiones de diseño, existen límites difusos entre las opciones (IMO). Vale la pena señalar que las extensiones solo se pueden realizar en clases estáticas de nivel superior, no en clases anidadas. Si tu enumeración es parte de una clase, necesitarás hacer una clase.
FreeText
51

También puede agregar un método de extensión al tipo Enum en lugar de una instancia de 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;
        }
    }
}

Puede invocar el método de extensión anterior haciendo:

var result = Enum<Duration>.Count;

No es un verdadero método de extensión. Solo funciona porque Enum <> es de un tipo diferente a System.Enum.

ShawnFeatherly
fuente
¿Puede la clase staticasegurarse de que todos sus métodos se comporten como extensiones?
Jatin Sanghvi
15
Para futuros lectores: la ambigüedad del nombre de Enum<T>es un poco confusa. También se podría llamar a EnumUtils<T>la clase y la llamada al método se resolvería en EnumUtils<Duration>.Count.
Namoshek
46

Por supuesto que puede, digamos, por ejemplo, que desea utilizar DescriptionAttribueen sus enumvalores:

using System.ComponentModel.DataAnnotations;

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

    [Description("Five days")]
    Week,

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

Ahora quieres poder hacer algo como:

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

Su método de extensión GetDescription()se puede escribir de la siguiente manera:

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;
}
Apilado
fuente
Estaba buscando crear una extensión para casi exactamente lo que hizo la muestra, excepto mi uso DisplayAttribute Localized GetDescription. saludos
George
Esta es una buena alternativa, aunque creo que el espacio de nombres es solo System.ComponentModel?
TomDestry
Buena perspectiva y gracias por mostrar la implementación y el código de extensión. Para acumular: con su implementación, también puede llamarlo así: var description = Duration.Week.GetDescription ();
Spencer Sullivan
31

Todas las respuestas son geniales, pero están hablando de agregar un método de extensión a un tipo específico de enumeración.

¿Qué sucede si desea agregar un método a todas las enumeraciones, como devolver un int del valor actual en lugar de la conversión 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;
    }
}

El truco detrás IConvertiblees su jerarquía de herencia ver MDSN

Gracias a ShawnFeatherly por su respuesta.

Basheer AL-MOMANI
fuente
1
la mejor respuesta!
Marco Alves
Ídem. Funciona si llamo a la extensión directamente (por ejemplo MyExtention.DoThing(myvalue)) pero en realidad no se adjunta a la enumeración (por ejemplo myvalue.DoThing())
Sinaesthetic
1
FYI, C # 7.3 ahora admite Enum como una restricción de tipo genérico
redtetrahedron
7

Puede crear una extensión para cualquier cosa, incluso object(aunque eso no se considera una práctica recomendada ). Comprenda un método de extensión solo como un public staticmétodo. Puede utilizar el tipo de parámetro que desee en los métodos.

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

Consulte MSDN .

public static class Extensions
{
  public static string SomeMethod(this Duration enumValue)
  {
    //Do something here
    return enumValue.ToString("D"); 
  }
}
LukeHennerley
fuente
7
Un voidvalor de retorno en una enumeración es algo extraño. Pensaría en una muestra más realista.
psubsee2003
3
@ psubsee2003 ¿el OP seguramente tiene suficiente conocimiento para cambiar esto para satisfacer sus necesidades? ¿Por qué es importante la muestra? Basta con responder a la pregunta inicial.
LukeHennerley
3
¿Soy el único que encuentra raros los ejemplos de código en MSDN? ¡La mayoría de las veces necesitas un esfuerzo real para comprender lo que están tratando de hacer!
Apilado el
0

acabamos de hacer una extensión de enumeración para c # https://github.com/simonmau/enum_ext

Es solo una implementación para typesafeenum, pero funciona muy bien, así que creamos un paquete para compartir, diviértete con él

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

Sé que el ejemplo es algo inútil, pero entiendes la idea;)

Simonmau
fuente
0

Una solución alternativa sencilla.

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
fuente