2010-03-11 21 views
40

Ho un'enumerazione per le mie cose in questo modo:Extension sulla enumerazione, non un'istanza di enumerazione

public enum Things 
{ 
    OneThing, 
    AnotherThing 
} 

Vorrei scrivere un metodo di estensione per questa enumerazione (simile a Prise's answer here), ma mentre che le opere di metodo su un un'istanza dell'enumerazione, ala

Things thing; var list = thing.ToSelectList(); 

vorrei farlo funzionare sul reale enumerazione invece:

var list = Things.ToSelectList(); 

ho potuto solo fare

var list = default(Things).ToSelectList(); 

Ma non mi piace il look di questo :)

ho ottenuto più stretto con il seguente metodo di estensione:

public static SelectList ToSelectList(this Type type) 
{ 
    if (type.IsEnum) 
    { 
     var values = from Enum e in Enum.GetValues(type) 
        select new { ID = e, Name = e.ToString() }; 
     return new SelectList(values, "Id", "Name"); 
    } 
    else 
    { 
     return null; 
    } 
} 

Usato così:

var list = typeof(Things).ToSelectList(); 

Possiamo fare meglio di così?

+0

Puoi creare una classe mascherata da enum. Gli svantaggi sono per lo più la programmazione non standard, credo, e l'incapacità di eliminare i metodi ereditati da Object. – Iucounu

+0

Non penso che sia una pratica corretta usare verbo per i nomi delle classi. idealmente il nome della classe dovrebbe essere sostantivo e significativo in sé. – user3264784

risposta

68

metodi di estensione funzionano solo su istanze, quindi non può essere fatto, ma con alcuni ben scelto i nomi delle classi/metodi e farmaci generici, si può produrre un risultato che sembra altrettanto buono:

public class SelectList 
{ 
    // Normal SelectList properties/methods go here 

    public static SelectList Of<T>() 
    { 
     Type t = typeof(T); 
     if (t.IsEnum) 
     { 
      var values = from Enum e in Enum.GetValues(type) 
         select new { ID = e, Name = e.ToString() }; 
      return new SelectList(values, "Id", "Name"); 
     } 
     return null; 
    } 
} 

allora si può ottenere la vostra lista di selezione in questo modo:

var list = SelectList.Of<Things>(); 

IMO questo legge molto meglio di Things.ToSelectList().

+3

questo è così geniale che in realtà ho riso forte quando l'ho letto. ero così concentrato nel fare un enum per una selezione che non avrei mai pensato di farlo al contrario. – spaceman

+0

Puoi creare una classe mascherata da enum. Gli svantaggi sono per lo più la programmazione non standard, credo, e l'incapacità di eliminare i metodi ereditati da Object. – Iucounu

+3

@Iucounu: Il rovescio della medaglia è ... non è un enume. Non si ottiene il controllo dell'intervallo, la rappresentazione numerica, l'archiviazione semplice del database, la conversione automatica delle stringhe o uno degli altri vantaggi di un enumerazione. Dati quegli svantaggi e nessun vero vantaggio, non riesco a pensare al motivo per cui vorresti farlo. – Aaronaught

5

No.

Il meglio che puoi fare è mettere su una classe statica, in questo modo:

public static class ThingsUtils { 
    public static SelectList ToSelectList() { ... } 
} 
1

@Aaronaught ha una risposta molto buona. Per estendere la sua risposta, puoi anche renderla più generica. Ho questo in una biblioteca globale ...

public static IQueryable GetAllEnumValues<T>() 
{ 
    IQueryable retVal = null; 

    Type targetType = typeof(T); 
    if(targetType.IsEnum) 
    { 
     retVal = Enum.GetValues(targetType).AsQueryable(); 
    } 

    return retVal; 
} 

Ora avete disaccoppiato questa funzionalità dalla classe SelectList. Quindi puoi chiamarlo nei tuoi metodi SelectList o in qualsiasi altro posto.

public class SelectList 
{ 
    public static SelectList Of<T> 
    { 
     IQueryable enumValues = GetAllEnumValues<T>(); 
     var values = 
      from Enum e in enumValues 
      select new { ID = e, Name = e.ToString() }; 
     return new SelectList(values, "Id", "Name"); 
    } 
} 
+0

Un'altra nota è che puoi usare la clausola where per migliorare la tua compilazione sui metodi generici per l'enumerazione ... GetAllEnumValues ​​() dove T: struct, IConvertible – coderob

1

Io uso "Tipo" invece di "Enum" per aggiungere l'estensione. Quindi posso recuperare qualsiasi tipo di lista dal metodo. Qui restituisce valori di stringa:

public static string[] AllDescription(this Type enumType) 
    { 
     if (!enumType.IsEnum) return null; 

     var list = new List<string>(); 
     var values = Enum.GetValues(enumType); 

     foreach (var item in values) 
     { 
      // add any combination of information to list here: 
      list.Add(string.Format("{0}", item)); 

      //this one gets the values from the [Description] Attribute that I usually use to fill drop downs 
      //list.Add(((Enum) item).GetDescription()); 
     } 

     return list.ToArray(); 
    } 

Più tardi ho potuto utilizzare questa sintassi per ottenere ciò che voglio:

var listOfThings = typeof (Things).AllDescription(); 
-1

Il più vicino si può venire, credo, è quello di manichino cose un po 'a lavorare come un enum senza essere uno.Ecco cosa mi è venuta in mente - sembra come un sacco di lavoro solo per plop un metodo statico su un'enumerazione, anche se capisco il fascino di programmazione di esso: risposta

public class PseudoEnum 
{ 
    public const int FirstValue = 1; 
    private static PseudoEnum FirstValueObject = new PseudoEnum(1); 

    public const int SecondValue = 2; 
    private static PseudoEnum SecondValueObject = new PseudoEnum(2); 

    private int intValue; 

    // This prevents instantation; note that we cannot mark the class static 
    private PseudoEnum() {} 

    private PseudoEnum(int _intValue) 
    { 
     intValue = _intValue; 
    } 

    public static implicit operator int(PseudoEnum i) 
    { 
     return i.intValue; 
    } 

    public static implicit operator PseudoEnum(int i) 
    { 
     switch (i) 
     { 
      case FirstValue : 
       return FirstValueObject; 
      case SecondValue : 
       return SecondValueObject; 
      default: 
       throw new InvalidCastException(); 
     } 
    } 

    public static void DoSomething(PseudoEnum pe) 
    { 
     switch (pe) 
     { 
      case PseudoEnum.FirstValue: 
       break; 
      case PseudoEnum.SecondValue: 
       break; 
     } 
    } 

} 
4

di Aaronaught è davvero grande, sulla base di tale ho fatto la seguente implementazione:

public class SelectList 
{ 
    public static IEnumerable<Enum> Of<T>() where T : struct, IConvertible 
    { 
     Type t = typeof(T); 
     if (t.IsEnum) 
     { 
      return Enum.GetValues(t).Cast<Enum>(); 
     } 
     throw new ArgumentException("<T> must be an enumerated type."); 
    } 
} 

a mio parere è un po 'più sicuro, come si può - quasi - lo chiamano solo con enumerazioni, e, naturalmente, al posto del tiro si può semplicemente restituire null se vuoi una versione senza eccezioni.

+0

Penso che tu voglia castarlo come T piuttosto che come Enum e restituire un oggetto IEnumerable maembe

0

A mio parere, questo è il modo più pulito. Perché?

  • funziona per qualsiasi System.Enum
  • Il metodo di estensione è di per sé più pulito.
  • chiamarlo si aggiunge solo new e questo è un piccolo compromesso (perché deve avere un'istanza al fine di lavorare.
  • Non sta passando null intorno e letteralmente non si compila se si tenta di usarlo con un altro tipo

utilizzati:.

(new Things()).ToSelectList() 

Metodo di estensione:

[Extension()] 
public SelectList ToSelectList(System.Enum source) 
{ 
    var values = from Enum e in Enum.GetValues(source.GetType) 
       select new { ID = e, Name = e.ToString() }; 
    return new SelectList(values, "Id", "Name");  
}