2012-03-12 20 views
11

La parte fondamentale della mia domanda è il salto. Ho intenzione di usare un tipo di enum che ha circa 20 elementi. Voglio ripetere questo set ma è necessario saltare un elemento o due ogni volta. Cosa saltare è noto in anticipo. Un esempio comparabile è il tipo enum che consiste in tutte le lettere dell'alfabeto e, quando iterando, voglio saltare tutte le vocali.Come ripetere il tipo enum saltando alcuni valori?

Come dovrei codificare l'iterazione in modo elegante/efficiente? Dovrei creare un insieme separato di elementi composti da vocali? Non ho codice da mostrare perché sto solo pensando al problema.

risposta

27
var query = Enum.GetValues(typeof(MyEnum)) 
    .Cast<MyEnum>() 
    .Except(new MyEnum[] { MyEnum.A, MyEnum.E }); 
foreach (MyEnum item in query) { 
    ... 
} 

È necessario lanciare al fine di ottenere la magia di LINQ. Except da solo non lo farà.


UPDATE:

ho avuto un'altra idea. È possibile definire l'enumerazione con lo FlagsAttribute e definire i valori normali come potenze di 2, ciò che si ottiene più facilmente con l'operatore di spostamento bit a bit di sinistra <<. A partire da C# 7.0, puoi anche utilizzare valori letterali binari come 0b_0000_0000_0010_0000. Quindi è possibile combinare i valori esistenti per formare nuovi valori.

[Flags] 
enum MyEnum 
{ 
    None = 0, 
    A = 1 << 0, 
    B = 1 << 1, 
    C = 1 << 2, 
    D = 1 << 3, 
    E = 1 << 4, 
    ... 
    X = 1 << 23, 
    Y = 1 << 24, 
    Z = 1 << 25, 
    Vowels = A | E | I | O | U 
} 

Ora, è possibile formulare la query come questa

IEnumerable<MyEnum> query = Enum.GetValues(typeof(MyEnum)) 
    .Cast<MyEnum>() 
    .Where(x => (x & MyEnum.Vowels) == MyEnum.None); 
foreach (MyEnum item in query) { 
    ... 
} 

Il vantaggio rispetto alla prima soluzione è, che è possibile eseguire il test con un singolo bit per bit AND-operazione.

È possibile definire fino a 32 potenze di due. Se è necessario altro, è possibile definire il tipo di base dell'enumerazione come long e utilizzare fino a 64 valori di flag (più combinazioni di valori di flag esistenti).

[Flags] 
enum MyEnum : long 
{ 
    ... 
} 
+0

Questa è probabilmente la risposta più completa. La cosa più facile da fare qui è specificare varie raccolte di Enum che vorresti saltare, in questo modo puoi avere quelle raccolte di salto salvate e possono passare in qualsiasi criterio tu voglia in Tranne. – SPFiredrake

+0

Sì, e puoi anche mantenere varie domande. –

+1

Ho aggiunto un'altra soluzione utilizzando solo un AND a bit per eseguire il test. Non richiede alcun array o Hashset per i valori eccezionali. Si prega di consultare il mio aggiornamento. –

1

Vorrei creare un insieme separato di elementi costituito da vocali e quindi prendere la differenza tra i due set utilizzando LINQ.

int[] vowels = {Letters.A, Letters.E, Letters.I, Letters.O, Letters.U}; 
IEnumerable<int> consonant = Enum.GetValues(typeof(Letters)).Except(vowels); 
foreach (int consonant in consonants) 
{ 
    // Do something with each consonant 
} 
1

probabilmente sarei basta usare LINQ - uso Enum.GetValues (o usare Unconstrained Melody - un enum libreria generica type-safe/delegato ho scritto) per ottenere tutti i valori, quindi esprimo i valori da mantenere/salta attraverso un Where clausola.

Se salti solo determinati valori, un HashSet o qualcosa di simile potrebbe essere utile (non ne vale la pena se ne stai saltando uno, ovviamente) il predicato -blown è richiesto.

Ad esempio:

public static IEnumerable<T> AllBut(T skipped) where T : struct 
{ 
    IEqualityComparer<T> comparer = EqualityComparer<T>.Default; 
    return AllBut<T>(t => !comparer.Equals(skipped, t)); 
} 

public static IEnumerable<T> AllBut(Func<T, bool> skipPredicate) where T : struct 
{ 
    IEqualityComparer<T> comparer = EqualityComparer<T>.Default; 
    return Enum.GetValues(typeof(T)) 
       .Cast<T>() 
       .Where(t => skipPredicate(t)); 
}