2010-10-11 2 views
59

Un modo più semplice di scrivere questo se dichiarazione?se le istruzioni corrispondono a più valori

if (value==1 || value==2)

Per esempio ... in SQL si può dire where value in (1,2) invece di where value=1 or value=2.

Sto cercando qualcosa che potesse funzionare con qualsiasi tipo di base ... string, int, ecc

+2

Più facile è una preferenza personale. Personalmente, non penso che sia più semplice di 'value == 1 || valore == 2'. –

+1

@Joel: È facile, ma non intuitivo per qualcuno che è solito dire "Voglio che sia 1 o 2". A quanto pare, ci sono stati linguaggi che implementano precisamente questa sintassi; FOCUS query IBI lanaguage, per uno. –

+0

@Steven Sudit - "Intuitivo" in questo contesto si riduce alle preferenze personali. –

risposta

86

ne dite:

if (new[] {1, 2}.Contains(value)) 

Si tratta di un hack però :)

O se non vi occupate di creare il proprio metodo di estensione, è possibile creare la seguente:

public static bool In<T>(this T obj, params T[] args) 
{ 
    return args.Contains(obj); 
} 

e si può usare in questo modo:

if (1.In(1, 2)) 

:)

+7

Non penso che il primo suggerimento sia davvero un hack in quanto tale, mi sembra abbastanza elegante, specialmente se si tratta di codice in sottofondo che molti chiamano tramite 'IsOneOfACertainType (typeToCheck);' ad esempio – Coops

+2

Do not forget per includere 'using System.Linq;' per usare 'Contains'. –

+1

Che * veramente * assomiglia a Python! – Panzercrisis

6

Se si dispone di un elenco, è possibile utilizzare .Contains (yourObject), se sei solo cercandolo esistente (come un dove). Altrimenti guarda il metodo di estensione Linq. Any().

3

In generale, no.

Sì, ci sono casi in cui l'elenco è in un Array o List, ma non è il caso generale.

22

E 'questo quello che stai cercando?

if (new int[] { 1, 2, 3, 4, 5 }.Contains(value)) 
5

utilizzando LINQ,

if(new int[] {1, 2}.Contains(value))

Ma avrei dovuto pensare che l'originale, se è più veloce.

+0

La differenza di prestazioni tra questi due è quasi sicuramente irrilevante. L'originale è più leggibile e idiomatico. È certamente possibile scrivere 'Enumerable.Range (0, 10) .ToList(). ForEach (x => Console.WriteLine (x));' invece di 'for (int i = 0; i <10; i ++) { Console.WriteLine (i); } 'ma questo non farà altro che far incazzare la gente. "Nessuno scrive mai che sia il gruppo 6". – jason

+0

@ Jason - Sono d'accordo che se vedessi quello che ho scritto nel codice di produzione, sarei incazzato. Era più di una "linea per spiegare un modo per farlo" che "segui questo esattamente". Davvero, sono d'accordo con le persone che suggeriscono le dichiarazioni switch. –

33

Un modo più complicato :) che emula 'IN' di SQL:

public static class Ext {  
    public static bool In<T>(this T t,params T[] values){ 
     foreach (T value in values) { 
      if (t.Equals(value)) { 
       return true; 
      } 
     } 
     return false; 
    } 
} 

if (value.In(1,2)) { 
    // ... 
} 

Ma andare per il modo standard, è più leggibile.

EDIT: una soluzione migliore, secondo @ suggerimento di Kobi:

public static class Ext {  
    public static bool In<T>(this T t,params T[] values){ 
     return values.Contains(t); 
    } 
} 
+0

richiede un tipo di reso, ma un metodo di estensione è quello che stavo per suggerire anche –

+1

@Daniel: sì, corretto, grazie :) –

+0

+1. Bella soluzione, proprio come la mia, ma la tua usando Generics. Sostituirò il mio con il tuo :) – goenning

1

Utilizzando metodi di estensione:

public static class ObjectExtension 
{ 
    public static bool In(this object obj, params object[] objects) 
    { 
     if (objects == null || obj == null) 
      return false; 
     object found = objects.FirstOrDefault(o => o.GetType().Equals(obj.GetType()) && o.Equals(obj)); 
     return (found != null); 
    } 
} 

Ora si può fare questo:

string role= "Admin"; 
if (role.In("Admin", "Director")) 
{ 
    ... 
} 
0
public static bool EqualsAny<T>(IEquatable<T> value, params T[] possibleMatches) { 
    foreach (T t in possibleMatches) { 
     if (value.Equals(t)) 
      return true; 
    } 
    return false; 
} 
public static bool EqualsAny<T>(IEquatable<T> value, IEnumerable<T> possibleMatches) { 
    foreach (T t in possibleMatches) { 
     if (value.Equals(t)) 
      return true; 
    } 
    return false; 
} 
5

In alternativa, e questo darebbe una maggiore flessibilità se il test sui valori diversi da 1 o 2, in futuro, è quello di utilizzare un'istruzione switch

switch(value) 
{ 
case 1: 
case 2: 
    return true; 
default: 
    return false 
} 
+0

Non direi nemmeno "alternativamente", questa è l'analogia più vicina all'utilizzo SQL di IN con una lista fornita nell'esempio ('Contains()' ecc. Corrispondente più a IN rispetto ai risultati di una sottoquery). –

1

Più facile è soggettivo, ma forse l'istruzione switch sarebbe più facile? Non è necessario ripetere la variabile, in modo che più valori possano adattarsi alla linea e una riga con molti confronti è più leggibile della controparte che utilizza l'istruzione if.

1

Un extensionmethod come questo sarebbe farlo ...

public static bool In<T>(this T item, params T[] items) 
{ 
    return items.Contains(item); 
} 

usare in questo modo:

Console.WriteLine(1.In(1,2,3)); 
Console.WriteLine("a".In("a", "b")); 
1

In vb.net o C# Mi aspetterei che l'approccio generale più veloce per confrontare un variabile rispetto a qualsiasi numero ragionevole di oggetti con nome separato (al contrario, ad esempio, di tutte le cose in una collezione) sarà semplicemente confrontare ogni oggetto con il confronto e molto come hai fatto. È certamente possibile creare un'istanza di una raccolta e vedere se contiene l'oggetto, e farlo potrebbe essere più espressivo che comparare l'oggetto con tutti gli oggetti individualmente, ma a meno che non si usi un costrutto che il compilatore può riconoscere esplicitamente, tale codice quasi certamente sarà molto più lento del semplice fare i confronti individuali. Non mi preoccuperei della velocità se il codice per sua natura funzionasse al massimo poche centinaia di volte al secondo, ma sarei diffidare dal fatto che il codice venga riproposto a qualcosa che viene eseguito molto più spesso di quanto inizialmente previsto.

Un approccio alternativo, se una variabile è qualcosa come un tipo di enumerazione, consiste nel scegliere valori di enumerazione power-of-two per consentire l'uso di maschere di bit. Se il tipo di enumerazione ha 32 o meno valori validi (ad esempio, iniziando Harry = 1, Ron = 2, Hermione = 4, Ginny = 8, Neville = 16) uno potrebbe memorizzarli in un numero intero e controllare più bit contemporaneamente in un singolo operazione ((if ((thisOne & (Harry | Ron | Neville | Beatrix))! = 0)/* Esegui qualcosa * /. Ciò consentirà un codice veloce, ma limitato alle enumerazioni con un numero limitato di valori.

Un approccio un po 'più potente, ma che deve essere usato con cautela, consiste nell'usare alcuni bit del valore per indicare gli attributi di qualcosa, mentre altri bit identificano l'elemento. Ad esempio, il bit 30 potrebbe indicare che un personaggio è maschio il bit 29 potrebbe indicare un amico di Harry ecc., mentre i bit più bassi distinguono tra i personaggi: questo approccio consentirebbe di aggiungere personaggi che potrebbero o meno essere amici di Harry, senza richiedere il codice che controlla per amico di Harry per cambiare. Un avvertimento è che bisogna fare una distinzione tra le costanti di enumerazione utilizzate per impostare un valore di enumerazione e quelle utilizzate per testarlo. Ad esempio, per impostare una variabile per indicare Harry, si potrebbe desiderare di impostarla su 0x60000001, ma per vedere se una variabile è Harry, si dovrebbe testare il bit con 0x00000001.

Un altro approccio, che può essere utile se il numero totale di valori possibili è moderato (ad esempio 16-16.000 o giù di lì) deve avere una matrice di flag associata a ciascun valore.Si potrebbe quindi codificare qualcosa come "if (((characterAttributes [theCharacter] & chracterAttribute.Male)! = 0)". Questo approccio funzionerà al meglio quando il numero di caratteri è piuttosto ridotto Se l'array è troppo grande, i messaggi di cache potrebbero rallentare giù il codice al punto che il test contro un piccolo numero di caratteri singolarmente sarebbe più veloce.

4

Se si cerca un valore in un elenco fisso di valori molte volte, HashSet <T> dovrebbe essere utilizzato, anche solo per HashSet mentre memorizza/cerca i dati nell'albero di ricerca binario

HashSet<int> nums = new HashSet<int> { 1, 2, 3, 4, 5 }; 
// .... 
if (nums.Contains(value)) 
0

Ho avuto lo stesso problema bu lo ha risolto con uno switch di stato (un valore che si sta attivando) { caso 1: il codice che si desidera verificare; caso 2: il codice che si desidera verificare; predefinito: restituire un valore }