Come aggiungere metodi di estensione a Enums


122

Ho questo codice Enum:

enum Duration { Day, Week, Month };

Posso aggiungere metodi di estensione per questo Enum?




risposta breve, sì. In questo caso specifico potresti prendere in considerazione l'uso diTimeSpan
Jodrell

1
Usare metodi di estensione su un enum mi farebbe sentire sporco. Crea una classe per incapsulare ciò che è necessario. Mantieni un enum il più semplice possibile. Se è necessaria più logica ad essa associata, creare una classe Duration che esponga giorno, settimana, mese e contenga qualsiasi altra logica che sarebbe stata nel metodo di estensione.
Jason Evans

2
Mi piace avere metodi di estensione enum per i gruppi di flag. Preferisco le clausole if, ad esempio, Day.IsWorkday()rispetto (Day & Days.Workday) > 0a quelle Days.Workdaydefinite come Monday | Tuesday ... | Friday. Il primo è più chiaro secondo me e ha esattamente il secondo implementato.
Sebastian Werk

Risposte:


114

Secondo questo sito :

I metodi di estensione forniscono un modo per scrivere metodi per le classi esistenti in un modo in cui altre persone del tuo team potrebbero effettivamente scoprire e utilizzare. Dato che le enumerazioni sono classi come le altre, non dovrebbe essere troppo sorprendente che tu possa estenderle, come:

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

Penso che le enumerazioni non siano la scelta migliore in generale, ma almeno questo ti consente di centralizzare alcuni degli interruttori / se gestirli e astrarli un po 'finché non puoi fare qualcosa di meglio. Ricordati di controllare che anche i valori rientrino nell'intervallo.

Puoi leggere di più qui su Microsft MSDN.


Credo che il commento "enums are evil" sia fuori luogo ma abbia una base nella realtà. Trovo che le enumerazioni possano essere un problema che viene abusato, poiché ti bloccano in determinati contesti e comportamenti.
Ed Schwehm

1
Le enumerazioni in C # fanno schifo perché compila ed esegue:Duration d = 0;
Graham

16
Given that enums are classesno non sono classi.
Ala Sendon

1
Uso questo tipo di estensione solo se riguarda direttamente l'enumerazione, come i giorni della settimana ei metodi di estensione IsWeekday (), IsWeekendday (). Ma le classi hanno lo scopo di incapsulare i comportamenti, quindi se ci sono molti o comportamenti complicati da incapsulare, una classe è probabilmente migliore. Se è limitato e di base, personalmente trovo che le estensioni sulle enumerazioni siano a posto. Come con la maggior parte delle decisioni di progettazione, ci sono confini sfocati tra le scelte (IMO). Vale la pena notare che le estensioni possono essere eseguite solo su classi statiche di primo livello, non su classi nidificate. Se la tua enumerazione fa parte di una classe, dovrai creare una classe.
FreeText

51

Puoi anche aggiungere un metodo di estensione al tipo Enum anziché un'istanza di 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;
        }
    }
}

Puoi richiamare il metodo di estensione sopra facendo:

var result = Enum<Duration>.Count;

Non è un vero metodo di estensione. Funziona solo perché Enum <> è un tipo diverso da System.Enum.


La classe può staticgarantire che tutti i suoi metodi si comportino come estensioni?
Jatin Sanghvi

15
Per i lettori futuri: l'ambiguità del nome di Enum<T>è un po 'confusa. La classe potrebbe anche essere chiamata EnumUtils<T>e la chiamata al metodo verrebbe risolta EnumUtils<Duration>.Count.
Namoshek

46

Ovviamente puoi, ad esempio, vuoi usare DescriptionAttribuesui tuoi enumvalori:

using System.ComponentModel.DataAnnotations;

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

    [Description("Five days")]
    Week,

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

Ora vuoi essere in grado di fare qualcosa come:

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

Il tuo metodo di estensione GetDescription()può essere scritto come segue:

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

stavo cercando di creare un'estensione per quasi esattamente quello che hai fatto, tranne il mio uso DisplayAttribute Localized GetDescription. applausi
George

Questa è una bella alternativa, anche se penso che lo spazio dei nomi sia solo System.ComponentModel?
TomDestry

Bella prospettiva e grazie per aver mostrato l'implementazione e il codice dell'estensione. Per accumulare: con la tua implementazione, puoi anche chiamarlo in questo modo: var description = Duration.Week.GetDescription ();
Spencer Sullivan

31

Tutte le risposte sono ottime, ma si tratta di aggiungere il metodo di estensione a un tipo specifico di enumerazione.

Cosa succede se si desidera aggiungere un metodo a tutte le enumerazioni come la restituzione di un int del valore corrente invece del casting esplicito?

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

Il trucco dietro IConvertibleè la sua gerarchia di ereditarietà, vedi MDSN

Grazie a ShawnFeatherly per la sua risposta


1
la migliore risposta!
Marco Alves

Idem. Funziona se chiamo l'estensione direttamente (ad esempio MyExtention.DoThing(myvalue)) ma in realtà non si collega myvalue.DoThing()
all'enumerazione

1
Cordiali saluti, C # 7.3 ora supporta Enum come vincolo di tipo generico
redtetrahedron

7

Puoi creare un'estensione per qualsiasi cosa, anche object( anche se non è considerata una best practice ). Comprendi un metodo di estensione proprio come un public staticmetodo. Puoi usare qualsiasi tipo di parametro che ti piace sui metodi.

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

5

Vedi MSDN .

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

7
Un voidvalore di ritorno su un'enumerazione è un po 'strano. Penserei a un campione più realistico.
psubsee2003

3
@ psubsee2003 l'OP ha sicuramente conoscenze sufficienti per cambiarlo in base alle sue esigenze? Perché il campione è importante, è sufficiente rispondere alla domanda iniziale.
LukeHennerley

3
Sono l'unico che trova strani gli esempi di codice su MSDN? Il più delle volte hai bisogno di un vero sforzo per capire cosa stanno cercando di fare!
Stacked

0

abbiamo appena creato un'estensione enum per c # https://github.com/simonmau/enum_ext

È solo un'implementazione per il typesafeenum, ma funziona benissimo, quindi abbiamo creato un pacchetto da condividere - divertiti con esso

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

So che l'esempio è un po 'inutile, ma hai capito;)


0

Una soluzione alternativa semplice.

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

        return ( int ) ( IConvertible ) payLoad;

    }
}

int num = YourEnum.AItem.ToInt();
Console.WriteLine("num : ", num);
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.