if-Anweisungen, die mit mehreren Werten übereinstimmen

78

Gibt es eine einfachere Möglichkeit, diese if-Anweisung zu schreiben?

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

Zum Beispiel ... in SQL können Sie where value in (1,2)statt sagen where value=1 or value=2.

Ich suche nach etwas, das mit jedem Basistyp funktioniert ... String, Int usw.

Ricky
quelle
2
Einfacher ist eine persönliche Präferenz. Persönlich denke ich nicht, dass es einfacher wird als value == 1 || value == 2.
Joel Etherton
1
@ Joel: Es ist einfach, aber nicht intuitiv für jemanden, der es gewohnt ist zu sagen "Ich möchte, dass es 1 oder 2 ist". Wie sich herausstellt, gab es Sprachen, die genau diese Syntax implementieren. Zum einen die FOCUS-Abfragesprache von IBI.
Steven Sudit
@Steven Sudit - "Intuitiv" in diesem Zusammenhang läuft auf persönliche Vorlieben hinaus.
Joel Etherton
@Steven Sudit - Ich muss zugeben, ich bin bisher ziemlich amüsiert über einige der lächerlichen Längen, die einige der Antworten nur zur Lösung eines einfachen oder.
Joel Etherton
5
Dies ist nur ein grundlegendes Beispiel für etwas weitaus Komplexeres. Ich arbeite nicht nur mit 2 Werten. Ich versuche auch, einige dieser Zeilen leichter lesbar zu machen. In einigen Fällen habe ich if-Anweisungen, die sich zu weit erstrecken und Dutzende möglicher Werte und lange Variablennamen haben.
Ricky

Antworten:

147

Wie wäre es mit:

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

Es ist allerdings ein Hack :)

Wenn es Ihnen nichts ausmacht, eine eigene Erweiterungsmethode zu erstellen, können Sie Folgendes erstellen:

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

Und Sie können es so verwenden:

if (1.In(1, 2))

:) :)

Amry
quelle
11
Ich denke nicht, dass der erste Vorschlag wirklich ein Hack als solcher ist, er scheint mir ziemlich elegant zu sein, besonders wenn es sich um Code im Hintergrund handelt, über den die meisten Leute IsOneOfACertainType(typeToCheck);zum Beispiel anrufen
Coops
8
Vergessen Sie nicht einzuschließen, um using System.Linq;zu verwenden Contains.
Guilherme de Jesus Santos
2
Das sieht wirklich nach Python aus!
Panzercrisis
36

Ein komplizierterer Weg :), der SQLs 'IN' emuliert:

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)) {
    // ...
}

Aber gehen Sie für den Standardweg, es ist besser lesbar.

EDIT : eine bessere Lösung, gemäß @ Kobis Vorschlag:

public static class Ext {    
    public static bool In<T>(this T t,params T[] values){
        return values.Contains(t);
    }
}
Paolo Tedesco
quelle
braucht einen Rückgabetyp, aber eine Erweiterungsmethode wollte ich auch vorschlagen
Daniel DiPaolo
+1. Schöne Lösung, genau wie meine, aber deine mit Generika. Ich werde meine durch deine ersetzen :)
am
Ich mag diese Version wirklich. Ich dachte, ich müsste vielleicht meine eigene Methode bauen.
Ricky
7
Könnten Sie nicht geschrieben haben return values.Contains(t)? Oder return values.Any(v => t.Equals(v))?
Kobi
27

Suchst du danach ?

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

Alternativ, und dies würde Ihnen mehr Flexibilität geben, wenn Sie in Zukunft auf andere Werte als 1 oder 2 testen, verwenden Sie eine switch-Anweisung

switch(value)
{
case 1:
case 2:
   return true;
default:
   return false
}
Jules
quelle
Ich würde nicht einmal "alternativ" sagen, dies ist die engste Analogie zur SQL-Verwendung von IN mit einer im Beispiel angegebenen Liste ( Contains()usw., die mehr mit IN gegen die Ergebnisse einer Unterabfrage korrespondiert).
Jon Hanna
6

Wenn Sie eine Liste haben, können Sie .Contains (yourObject) verwenden, wenn Sie nur nach einer vorhandenen suchen (z. B. einem Where). Ansonsten schauen Sie sich die Erweiterungsmethode Linq .Any () an.

Nik
quelle
5

Mit Linq,

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

Aber ich müsste denken, dass Ihr Original schneller ist.

Joel Rondeau
quelle
Der Leistungsunterschied zwischen diesen beiden ist mit ziemlicher Sicherheit irrelevant. Das Original ist besser lesbar und idiomatisch. Es ist sicherlich möglich zu schreiben Enumerable.Range(0, 10).ToList().ForEach(x => Console.WriteLine(x));anstatt, for(int i = 0; i < 10; i++) { Console.WriteLine(i); }aber das wird die Leute nur verärgern. "Niemand schreibt jemals, lass 6eine Gruppe sein."
Jason
@ Jason - Ich stimme zu, wenn ich sehen würde, was ich im Produktionscode geschrieben habe, wäre ich sauer. War eher eine "eine Zeile, um einen Weg zu erklären, wie man es macht" als "folge genau diesem". Wirklich, ich stimme den Leuten zu, die die switch-Anweisungen vorschlagen.
Joel Rondeau
5

Wenn Sie einen Wert in einer festen Werteliste mehrmals in einer langen Liste suchen , sollte HashSet <T> verwendet werden. Wenn die Liste sehr kurz ist (<~ 20 Elemente), kann List eine bessere Leistung aufweisen, basierend auf diesem Test der Leistung von HashSet vs. List

HashSet<int> nums = new HashSet<int> { 1, 2, 3, 4, 5 };
// ....
if (nums.Contains(value))
detale
quelle
:) Dies ist eine interessante Aussage über HashSet... Wenn man bedenkt, dass es nach seiner Implementierung der Datenspeicherung benannt ist, warum hat es Ihrer Meinung nach irgendwie mit BST zu tun?
Alexei Levenkov
@AlexeiLevenkov :) Danke, dass du darauf hingewiesen hast. Ich habe etwas gesucht, die .NET-Version von HashSet basiert nur auf linearen Algorithmen. Im Vergleich dazu verwendet HashMap von Java 8 eine selbstausgleichende BST.
Detale
3

Im Allgemeinen nein.

Ja, es gibt Fälle, in denen sich die Liste in einem Arrayoder befindet List, aber das ist nicht der allgemeine Fall.

Steven Sudit
quelle
2

Eine Erweiterungsmethode wie diese würde es tun ...

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

Verwenden Sie es so:

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

Einfacher ist subjektiv, aber vielleicht wäre die switch-Anweisung einfacher? Sie müssen die Variable nicht wiederholen, damit mehr Werte in die Zeile passen und eine Zeile mit vielen Vergleichen besser lesbar ist als das Gegenstück mit der if-Anweisung.

Tim
quelle
1

In vb.net oder C # würde ich erwarten, dass der schnellste allgemeine Ansatz zum Vergleichen einer Variablen mit einer angemessenen Anzahl von Objekten mit separatem Namen (im Gegensatz zu z. B. allen Dingen in einer Sammlung) darin besteht, jedes Objekt einfach mit dem Vergleich zu vergleichen wie du es getan hast. Es ist sicherlich möglich, eine Instanz einer Sammlung zu erstellen und zu prüfen, ob sie das Objekt enthält. Dies ist möglicherweise aussagekräftiger als der Vergleich des Objekts mit allen Elementen einzeln, es sei denn, man verwendet ein Konstrukt, das der Compiler explizit erkennen kann, einen solchen Code wird mit ziemlicher Sicherheit viel langsamer sein als nur die einzelnen Vergleiche. Ich würde mir keine Sorgen um die Geschwindigkeit machen, wenn der Code von Natur aus höchstens ein paar hundert Mal pro Sekunde ausgeführt wird, aber ich wäre vorsichtig, wenn der Code für etwas verwendet würde, das '

Ein alternativer Ansatz, wenn eine Variable so etwas wie ein Aufzählungstyp ist, besteht darin, Zweierpotenz-Aufzählungswerte zu wählen, um die Verwendung von Bitmasken zu ermöglichen. Wenn der Aufzählungstyp 32 oder weniger gültige Werte hat (z. B. Harry = 1, Ron = 2, Hermine = 4, Ginny = 8, Neville = 16), könnte man sie in einer ganzen Zahl speichern und in einem einzigen auf mehrere Bits gleichzeitig prüfen operation ((if ((thisOne & (Harry | Ron | Neville | Beatrix))! = 0) / * Mach etwas * /. Dies ermöglicht schnellen Code, ist jedoch auf Aufzählungen mit einer kleinen Anzahl von Werten beschränkt.

Ein etwas leistungsfähigerer Ansatz, der jedoch mit Vorsicht angewendet werden muss, besteht darin, einige Bits des Werts zu verwenden, um Attribute von etwas anzuzeigen, während andere Bits das Element identifizieren. Zum Beispiel könnte Bit 30 anzeigen, dass ein Zeichen männlich ist, Bit 29 könnte einen Freund von Harry usw. anzeigen, während die unteren Bits zwischen Zeichen unterscheiden. Dieser Ansatz würde das Hinzufügen von Charakteren ermöglichen, die möglicherweise ein Freund von Harry sind oder nicht, ohne dass der Code, der nach Freunden von Harry sucht, geändert werden muss. Eine Einschränkung dabei ist, dass man zwischen Aufzählungskonstanten, die zum Einstellen eines Aufzählungswerts verwendet werden, und solchen, die zum Testen verwendet werden, unterscheiden muss. Um beispielsweise eine Variable festzulegen, die Harry anzeigt, möchten Sie sie möglicherweise auf 0x60000001 setzen. Um jedoch zu sehen, ob eine Variable Harry ist, sollten Sie sie mit 0x00000001 testen.

Ein weiterer Ansatz, der nützlich sein kann, wenn die Gesamtzahl der möglichen Werte moderat ist (z. B. 16-16.000 oder so), besteht darin, jedem Wert ein Array von Flags zuzuordnen. Man könnte dann so etwas wie "if (((characterAttributes [theCharacter] & chracterAttribute.Male)! = 0)" codieren. Dieser Ansatz funktioniert am besten, wenn die Anzahl der Zeichen relativ klein ist. Wenn das Array zu groß ist, können Cache-Fehler langsamer werden Reduzieren Sie den Code bis zu dem Punkt, dass das Testen mit einer kleinen Anzahl von Zeichen einzeln schneller wäre.

Superkatze
quelle
1

Verwenden von Erweiterungsmethoden:

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

Jetzt können Sie dies tun:

string role= "Admin";
if (role.In("Admin", "Director"))
{ 
    ...
} 
goenning
quelle
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;
}
JWL_
quelle
-1

Ich hatte das gleiche Problem, löste es aber mit einem switch-Anweisungsschalter (ein Wert, den Sie einschalten) {Fall 1: Der Code, den Sie ausführen möchten; Fall 2: der Code, der passieren soll; Standard: Wert zurückgeben}

Ernest www.ernestech.com
quelle