Entwickler-Ecke

C# - Die Sprache - == - Vergleiche mit mehreren Werten


erfahrener Neuling - Di 17.05.16 13:04
Titel: == - Vergleiche mit mehreren Werten
Hallo,

gibt es eine Möglichkeit, eine Bedingung ungefähr nach folgendem Schema abzufragen

C#-Quelltext
1:
if (myString == ("abc" || "123" || "xyz")) {...}                    
anstatt

C#-Quelltext
1:
if (myString == "abc" || myString == "123" || myString == "xyz") {...}                    

Bin gespannt auf eure Antworten


Ralf Jansen - Di 17.05.16 13:13


C#-Quelltext
1:
if ((new string[] { "abc""123""xyz" }).Contains(myString))                    


Nebenbei solltest du dich an der Stelle aber fragen was für einen Vergleich du genau willst.
Bei strings muß man sich immer fragen mit welchen Culture Einstellungen das gerade passiert und ob man casesensitiv/insensitiv prüfen will.

Die verschiedenen Möglichkeiten zum Vergleichen von string funktionieren nicht unbedingt gleich.


erfahrener Neuling - Di 17.05.16 13:26

Ok so kann man's auch machen, ist mir aber zu lang. Schade, dass es keine kürzere Variante gibt, aber man kann ja drauf warten.
Zitat:
Bei strings muß man sich immer fragen mit welchen Culture Einstellungen das gerade passiert
Ich momentan noch nicht (zum Glück) ;)


Ralf Jansen - Di 17.05.16 13:35

Zitat:
Ok so kann man's auch machen, ist mir aber zu lang


Nun ja ich habs nur zu Ansichtszwecken inline gemacht. Wenn du das so wie von dir angegeben machen wolltest brauchst du ja eh eine Konstante Liste.
Da kann man dann auch wunderbar hingehen sich das string Array als Konstante vorzudefinieren und zur Seite legen. Wenn man dem dann noch einen schönen sprechenden Namen gibt hat man was wirklich lesbar.
Lesbarer als jede Aufzählung von strings in der if Abfrage je sein kann.


erfahrener Neuling - Di 17.05.16 13:50

Da hast du recht!


Palladin007 - Di 17.05.16 19:18

Als kleine Rand-Info: Man kann die Array-Definition auch etwas kürzen. Die runten Klammern darum und der Typ können weg:


C#-Quelltext
1:
if (new [] { "abc""123""xyz" }.Contains(myString))                    


Wenn Du es ganz extrem willst, kannst Du dir ja auch mit einer Erweiterungsmethode behelfen:


C#-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
public static class Extend_Object
{
    public static bool In<T>(this T source, params T[] items)
    {
        return items.Contains(source);
    }
    public static bool In<T>(this T source, IEqualityComparer<T> comparer, params T[] items)
    {
        return items.Contains(source, comparer);
    }
}
Und die Nutzung:

C#-Quelltext
1:
if (myString.In("abc""123""xyz"))                    

Für Strings kann dann ja noch eine Überladung hinzugefügt werden, welche die StringComparison bekommt.

Besser finde ich das aber nicht, sondern eher nach unnötigen Code.
Ich würde schon zu der Array-Variante von Ralf tendieren, aber das ist ja auch alles Geschmackssache.


erfahrener Neuling - Mi 18.05.16 13:51

die gekürzte Array-Variante gefällt mir persöhlich am besten :zustimm:

Kann mir jemand erklären wo/wie bei der Klasse Extend_Object der Bezug zu einem string hergestellt wird? (Also das man mystring.In schreiben kann)
In ist ja ansich keine Methode der string-Klasse.


C# - Mi 18.05.16 14:41

Das liegt daran, dass In<T> eine Extension Methode ist. Das Schlüsselwort dafür ist das this in der Parameterdefinition. Außerdem muss die entsprechende Methode statisch sein.

C#-Quelltext
1:
2:
3:
4:
    public static bool In<T>(this T source, params T[] items)
    {
        return items.Contains(source);
    }


Die Version von Palladin007 ist generisch gehalten und kann daher z.B. auch für intlongdoubleobject oder einen anderen, beliebigen Typ verwendet werden.

Speziell für Strings würde die Methode so aussehen:

C#-Quelltext
1:
2:
3:
4:
    public static bool In(this string source, params string[] items)
    {
        return items.Contains(source);
    }


Ralf Jansen - Mi 18.05.16 14:42

Das Feature schimpft sich Extension Methods(Erweiterungsmethoden) [https://msdn.microsoft.com/de-de/library/bb383977.aspx] (entscheidend ist das this am ersten Parameter)
Und 2.tens wird hier das implizite deduzieren des generischen Parameters gebraucht für die generischen in Methoden. Wenn string.In aufgerufen wird weiß der Compiler das für den generischen Typen T string zu nehmen ist.

Beispiel mit den Aufrufmöglichkeiten der Methode


C#-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
static void Main(string[] args)
{
    string myString = "123";
    string[] myStrings = new[] { "abc""123""xyz" };
            
    Extend_Object.In<string>(myString, myStrings);  // expliziter Methodenaufruf 

    Extend_Object.In(myString, myStrings); // expliziter Methodenaufruf der generische Parameter T wird aus erstem Parameter deduziert. (T -> string) 

    myString.In<string>(myStrings); // Aufruf als Erweiterungsmethode

    myString.In(myStrings);  // Aufruf als Erweiterungsmethode generischer parameter T aus erstem Parameter (der jetzt Augrund des Erweiterungsmethoden Features jetzt links vom Punkt steht) deduziert.
}


Alle 4 Aufrufe erzeugen absolut identischen Laufzeitcode. Aber letzteres ist natürlich deutlich besser lesbar. Das ist reine Magie des Editors nichts was tief im Compiler versteckt wäre sondern nur den Aufrufsyntax vereinfacht.


erfahrener Neuling - Mi 18.05.16 15:06

coole Sache :)


Palladin007 - Mi 18.05.16 18:51

Als kleine Ergänzung:

Die Klasse, in der die Extension-Method ist, muss ebenfalls statisch sein.



Besonders hilfreich ist sowas bei Typen, die nicht mehr erweitert werden können, also vorhandene Typen, wo der Source-Code nicht vorliegt oder Typen, die keine eigene Funktionalität enthalten können, wie z.B. Enums oder Interfaces.
Außerdem nutze ich das ganz gerne für Überladungen von Methoden. Ich setze dann in der eigentlichen Klasse eine "Haupt"-Methode um und dazu schreibe ich noch ein paar Erweiterungs-Methoden, die den Aufruf der "Haupt"-Methode vereinfachen. So reduziere ich den Code in der Klasse auf das wesentliche für die Klasse Notwendige, während alle Vereinfachungen in einer eigenen Klasse liegen.

Als kleiner Tipp: Klassen mit Extension-Methods sollten (mMn) immer möglichst weit "oben" im Namespace liegen, denn theoretisch kannst Du ein Objekt von einem Typen irgendwie bekommen, ohne ein using für das Namespace zu haben. Du brauchst aber dieses Namespace um die Extension-Method zu finden. Um das zu umgehen, lege ich Klassen mit ExtensionMethods möglichst weit oben im Namespace an.
Außerdem habe ich mich für die Namen für das Format "Extend_NameDesTyps" entschieden. Den Klassen-Namen selber nutze ich nie direkt, so lässt sich die Klasse aber leicht von Anderen unterscheiden.


Ralf Jansen - Mi 18.05.16 19:06

Zitat:
Außerdem habe ich mich für die Namen für das Format "Extend_NameDesTyps" entschieden.


Bei einem generischen Typen schwer durchzuhalten ;)


Palladin007 - Mi 18.05.16 19:26

Bei generischen Typen denke ich mir den generischen Typ-Parameter immer weg ^^

Wenn ich z.B. zwei Klassen mit dem selben Namen habe, eine davon aber generisch, dann lege ich die auch in die selbe Datei. ZUmindest solange die Datei nicht zu groß wird :D
Und genauso mache ich das auch bei ExtensionMethods ^^