2010-08-09 4 views
19

Ich habe eine harte Zeit zu finden, was, denke ich, sollte eine ziemlich einfache Methode sein.C# In() Methode? (wie Sql)

Ich denke, wir haben alle benutzten:

select someThing from someTable where someColumn in('item1', 'item2') 

In C#, ich habe Sachen wie diese schreiben:

if (someEnum == someEnum.Enum1 || someEnum == someEnum.Enum2 || 
    someEnum == someEnum.Enum3) 
{ 
    this.DoSomething(); 
} 

Dies funktioniert, aber es ist nur wortreich.

Aus Frustration habe ich eine Erweiterungsmethode geschrieben, um zu erreichen, was ich versuche zu tun.

namespace System 
{ 
    public static class SystemExtensions 
    { 
     public static bool In<T>(this T needle, params T[] haystack) 
     { 
      return haystack.Contains(needle); 
     } 
    } 
} 

Jetzt kann ich kürzeren Code schreiben:

if (someEnum.In(someEnum.Enum1, someEnum.Enum2, someEnum.Enum3)) 
    this.DoSomething(); 
if (someInt.In(CONSTANT1, CONSTANT2)) 
    this.DoSomethingElse(); 

Es fühlt sich schmutzig, aber auf meine eigene Methode für etwas zu schreiben, ich kann einfach nicht im Rahmen finden.

Jede Hilfe Sie Leute anbieten wäre toll, Dank

EDIT: Vielen Dank allen für die eingehende anaylsis. Ich denke, ich werde weiterhin meine In() -Methode verwenden.

+0

Es gibt nichts im Framework, um dies zu tun, also scheint Ihr Ansatz solide. du könntest auch new [] {someEnum.Enum1, someEnum.Enum2} .Contains (someEnum} wenn du die extension-Methode nicht willst. Die extension-Methode macht es aber netter. – btlog

+3

Ich benutze generell '' Contains() ' Erweiterungsmethode für * In * -Abfragen Tatsächlich wird 'Contains()' in Linq-zu-SQL-Abfragen in 'in' übersetzt.Ich mag Ihre 'In()' Erweiterung Methode, wie es eine schöne syntaktische Wrapper für 'Enthält()' zur Verwendung, wenn ich nicht bereits die Werte in einem aufzählbaren Container haben. – kbrimington

+0

Es sieht so aus, als ob Sie ziemlich genau herausgefunden haben, warum Erweiterungsmethoden überhaupt vorhanden sind. Ich mag die Sauberkeit Ihres Ansatzes. –

Antwort

7

Es gibt keine Extension-Methode wie das, was Sie haben. Lassen Sie mich erklären, warum ich das denke (abgesehen von der offensichtlichen "weil es nicht spezifiziert, implementiert, getestet, dokumentiert, etc." Grund).

Grundsätzlich ist diese Implementierung notwendigerweise ineffizient. Das Konstruieren eines Arrays aus den an In übergebenen Parametern (wie es passiert, wenn Sie das Schlüsselwort params verwenden) ist eine O (N) -Operation und verursacht einen unnötigen GC-Druck (von der Konstruktion eines neuen Objekts T[]). Contains zählt dann über dieses Array, was bedeutet, dass Ihr ursprünglicher Code in der Ausführungszeit mehr als verdoppelt wurde (statt einer partiellen Aufzählung über eine kurzgeschlossene Auswertung haben Sie eine vollständige Aufzählung gefolgt von einer teilweisen Aufzählung).

Der Druck GC durch die Arraykonstruktion verursacht könnte etwas von 1 bis X Parameter vom Typ mit X Überlastungen Einnahme T durch Ersetzen der params Version des Erweiterungsmethode gelindert wird, in denen X eine vernünftige Zahl ist wie 1 ... -2 Dutzend. Aber das ändert nichts an der Tatsache, dass Sie X-Werte auf eine neue Ebene des Aufruf-Stacks übergeben, nur um potentiell weniger als X von ihnen zu überprüfen (d. H. Es beseitigt nicht die Leistungseinbußen, sondern reduziert sie nur).

Und dann gibt es ein anderes Problem: Wenn Sie beabsichtigen, für diese In Erweiterungsmethode als Ersatz für eine Reihe von verketteten || Vergleiche dienen, gibt es noch etwas, das Sie vielleicht übersehen. Mit || erhalten Sie eine kurzgeschlossene Auswertung; Das Gleiche gilt nicht für Parameter, die an Methoden übergeben werden. Im Falle eines enum, wie in Ihrem Beispiel, spielt dies keine Rolle. Aber betrachten Sie diesen Code:

if (0 == array.Length || 0 == array[0].Length || 0 == array[0][0].Length) 
{ 
    // One of the arrays is empty. 
} 

Die oben (komisch/schlecht - nur für Abbildung) Code sollte nicht werfen ein IndexOutOfRangeException (es ein NullReferenceException werfen könnte, aber das ist irrelevant für den Punkt, den ich machen). Allerdings könnte das „Äquivalent“ Code In sehr gut:

if (0.In(array.Length, array[0].Length, array[0][0].Length) 
{ 
    // This code will only be reached if array[0][0].Length == 0; 
    // otherwise an exception will be thrown. 
} 

Ich bin nicht Ihre In Erweiterung Idee ist, ein schlechtes sagen. In den meisten Fällen kann es, wenn es richtig verwendet wird, beim Schreiben sparen, und die Leistungs-/Speicherkosten sind nicht bemerkbar. Ich gebe nur meine Gedanken darüber, warum eine Methode dieser Art als integrierte Bibliotheksmethode nicht geeignet wäre: weil ihre Kosten und Einschränkungen wahrscheinlich falsch verstanden würden, was zu übermäßiger Nutzung und suboptimalem Code führen würde.

+0

Woher weißt du, ist O (N)? – mathk

+0

@mathk - Nun, wenn Sie n Elemente haben, müssen Sie jedes dieser n Elemente zum Array hinzufügen. Es gibt keine Möglichkeit, Gegenstände zu ignorieren. Wenn Sie dies tun, haben Sie per Definition kein Array mit allen n Elementen erstellt. Also muss es mindestens O (n) sein. – jloubert

+0

Da es sich um ein unveränderliches Array handelt, können Sie es auf dem Stapel lassen, ohne Objekte verschieben zu müssen. Donno, wie C# es geschafft haben, aber es könnte sein O (1) – mathk

0

Sie könnten an der FlagAttibute interessiert sein, wenn Sie dies besonders mit Enums tun möchten.

+0

Dieses Attribut hat nur kosmetische Semantik. – leppie

+0

@leppie Das Framework verwendet dieses Attribut in der ToString-Implementierung der Enumeration, wenn der Enumerant keinem Wert entspricht, und ist als Dokumentation nützlich. – Trillian

+1

-1 Es wird überhaupt nicht helfen. Das Flags-Attribut behandelt ein vollständig orthogonales Problem, nämlich die Zusammensetzung mehrerer Enumerationswerte in einer einzelnen Variablen. Das OP hat nur einen Wert in der Variablen. – siride

0

Sprachen können nicht jedem gefallen, aber ob Sie es tun, oder der Compiler tut es dort ist nicht viel Unterschied. Die Sprache gibt Ihnen Any & Contains

In vielleicht in Ihrer Welt schön sein, aber wenn jemand anderes Ihren Code zu holen hat, wird es ihnen verwirrend sein.

1

Ich denke, Sie sind in der Nähe mit dem Contains Aufruf.

List<strong> items = List<string>{ "item1", "item2", "item3" }; 
bool containsItem = items.Contains("item2"); 

Dies ist der übliche Ansatz für Linq-Abfragen.

BTW: Ich mag Ihre Erweiterungsmethode, ich denke, das könnte in bestimmten Situationen sehr nützlich sein.

+0

er verwendet Contains in seiner Erweiterungsmethode. –

1

Das ist es ziemlich viel. Ihre Erweiterungsmethode In() ist ziemlich nett. Selbst wenn Sie LINQ verwenden, das nach SQL modelliert ist, müssen Sie immer Contains verwenden, um anzugeben, dass in SQL IN verwendet wird.

from a in table 
where SomeArray.Contains(a.id) 
select a; 

Verschiebt zu:

select * from table a where a.id in (.....) 
1

Ich weiß nicht, irgendetwas anderes.

Für mich, ich denke, es ist nur in Ordnung, solche Erweiterungsmethoden wie Sie zu schreiben, für Operationen, die Sie oft brauchen, und Sie wollen eine lesbare und handliche Syntax. Dafür sind Erweiterungsmethoden gut.

Es gibt nur Hunderte von nützlichen Erweiterungsmethoden um.Sie könnten nach vielen von ihnen fragen, warum sind sie nicht im .NET-Framework enthalten?

Nicht alles kann bereits in einer Sprache enthalten sein. Schreiben Sie also Ihre eigene Bibliothek und hoffen Sie, dass sie in die Zukunft aufgenommen wird.

0

Sie könnten etwas besser machen, indem Sie Ausdrücke verwenden, damit das Konstrukt in Fällen wie Linq2Sql ordnungsgemäß verwendet werden kann.

0

Sie können die Erweiterungsmethode .Intersect verwenden, wenn Sie unterschiedliche Werte zurückgegeben möchten. Z.B.

List<string> test = new List<string>() { "1", "2", "2", "3" }; 
List<string> test2 = new List<string>() { "1", "2" }; 

var results = test.Intersect<string>(test2);