Gibt es eine übliche Möglichkeit, ein einzelnes Element vom Typ T
an eine Methode zu übergeben, die einen IEnumerable<T>
Parameter erwartet ? Die Sprache ist C #, Framework Version 2.0.
Derzeit verwende ich eine Hilfsmethode (es ist .Net 2.0, daher habe ich eine ganze Reihe von Hilfsmethoden zum Casting / Projizieren, die LINQ ähneln), aber das scheint einfach albern:
public static class IEnumerableExt
{
// usage: IEnumerableExt.FromSingleItem(someObject);
public static IEnumerable<T> FromSingleItem<T>(T item)
{
yield return item;
}
}
Ein anderer Weg wäre natürlich, ein List<T>
oder ein zu erstellen und zu füllen Array
und es stattdessen zu übergeben IEnumerable<T>
.
[Bearbeiten] Als Erweiterungsmethode könnte es heißen:
public static class IEnumerableExt
{
// usage: someObject.SingleItemAsEnumerable();
public static IEnumerable<T> SingleItemAsEnumerable<T>(this T item)
{
yield return item;
}
}
Vermisse ich hier etwas?
[Edit2] Wir haben festgestellt someObject.Yield()
(wie @Peter in den Kommentaren unten vorgeschlagen hat), dass dies der beste Name für diese Erweiterungsmethode ist, hauptsächlich aus Gründen der Kürze. Hier ist es also zusammen mit dem XML-Kommentar, wenn jemand ihn greifen möchte:
public static class IEnumerableExt
{
/// <summary>
/// Wraps this object instance into an IEnumerable<T>
/// consisting of a single item.
/// </summary>
/// <typeparam name="T"> Type of the object. </typeparam>
/// <param name="item"> The instance that will be wrapped. </param>
/// <returns> An IEnumerable<T> consisting of a single item. </returns>
public static IEnumerable<T> Yield<T>(this T item)
{
yield return item;
}
}
quelle
if (item == null) yield break;
Jetzt können Sie keine Null mehr übergeben und das (triviale) Null-Objektmuster für nutzenIEnumerable
. (foreach (var x in xs)
behandelt eine leerexs
ganz gut). Übrigens ist diese Funktion die monadische Einheit für die ListenmonadeIEnumerable<T>
, und angesichts des Monaden-Liebesfestes bei Microsoft bin ich überrascht, dass so etwas überhaupt nicht im Framework enthalten ist.AsEnumerable
da bereits eine integrierte Erweiterung mit diesem Namen vorhanden ist . (WennT
implementiertIEnumerable
, zstring
. B. )Yield
? Nichts geht über die Kürze.left==null
Scheck hier. Es bricht die Schönheit des Codes und verhindert, dass der Code flexibler wird - was ist, wenn Sie eines Tages einen Singleton mit etwas generieren müssen, das null sein kann? Ich meine,new T[] { null }
ist nicht dasselbe wienew T[] {}
, und eines Tages müssen Sie sie möglicherweise unterscheiden.Antworten:
Ihre Hilfsmethode ist der sauberste Weg, IMO. Wenn Sie eine Liste oder ein Array übergeben, kann ein skrupelloser Code diese umwandeln und den Inhalt ändern, was in einigen Situationen zu merkwürdigem Verhalten führt. Sie könnten eine schreibgeschützte Sammlung verwenden, dies erfordert jedoch wahrscheinlich noch mehr Wrapping. Ich denke, Ihre Lösung ist so ordentlich wie es nur geht.
quelle
myDict.Keys.AsEnumerable()
womyDict
war der TypDictionary<CheckBox, Tuple<int, int>>
. Nachdem ich die Erweiterungsmethode in umbenannt hatteSingleItemAsEnumerable
, fing alles an zu funktionieren. IEnumerable <Dictionary <CheckBox, System.Tuple <int, int >>. KeyCollection> 'kann nicht in' IEnumerable <CheckBox> 'konvertiert werden. Es gibt eine explizite Konvertierung (fehlt Ihnen eine Besetzung?) Ich kann eine Weile mit einem Hack leben, aber können andere Power-Hacker einen besseren Weg finden?dictionary.Values
damit anfangen. Grundsätzlich ist nicht klar, was los ist, aber ich schlage vor, Sie beginnen eine neue Frage darüber.Nun, wenn die Methode erwartet, dass
IEnumerable
Sie etwas übergeben müssen, das eine Liste ist, auch wenn es nur ein Element enthält.Vorbeigehen
da sollte das argument ausreichen sollte ich denke
quelle
In C # 3.0 können Sie die System.Linq.Enumerable-Klasse verwenden:
Dadurch wird eine neue IEnumerable erstellt, die nur Ihren Artikel enthält.
quelle
new[]{ item }
mich eigentlich nur selbst, ich dachte nur, das wäre eine interessante Lösung.In C # 3 (ich weiß, dass Sie 2 gesagt haben) können Sie eine generische Erweiterungsmethode schreiben, die die Syntax möglicherweise etwas akzeptabler macht:
Client-Code ist dann
item.ToEnumerable()
.quelle
ToEnumerable
System.Linq.Enumerable.AsEnumerable
ToEnumerable
Erweiterungsmethode fürIObservable<T>
, sodass dieser Methodenname auch bestehende Konventionen beeinträchtigt. Ehrlich gesagt würde ich vorschlagen, überhaupt keine Erweiterungsmethode zu verwenden, einfach weil sie zu allgemein ist.Diese Hilfsmethode funktioniert für Elemente oder viele.
quelle
params
, das Array zu erstellen, so dass der resultierende Code sauber aussieht. Ich habe nicht entschieden, ob es mir mehr oder weniger gefällt alsnew T[]{ item1, item2, item3 };
für mehrere Artikel.Ich bin etwas überrascht, dass niemand eine neue Überladung der Methode mit einem Argument vom Typ T vorgeschlagen hat, um die Client-API zu vereinfachen.
Jetzt kann Ihr Client-Code Folgendes tun:
oder mit einer Liste:
quelle
DoSomething<T>(params T[] items)
was bedeutet, dass der Compiler die Konvertierung von einem einzelnen Element in ein Array übernehmen würde. (Dies würde es Ihnen auch ermöglichen, mehrere separate Elemente zu übergeben, und der Compiler würde wiederum die Konvertierung dieser Elemente in ein Array für Sie übernehmen.)Entweder (wie bereits gesagt)
oder
Als Randnotiz kann die letzte Version auch schön sein, wenn Sie eine leere Liste eines anonymen Objekts möchten, z
quelle
Wie ich gerade gefunden und gesehen habe, dass Benutzer LukeH auch vorgeschlagen hat, ist eine schöne einfache Möglichkeit, dies zu tun, wie folgt:
Mit diesem Muster können Sie dieselbe Funktionalität auf vielfältige Weise aufrufen: ein einzelnes Element; mehrere Elemente (durch Kommas getrennt); eine Anordnung; eine Liste; eine Aufzählung usw.
Ich bin mir nicht 100% sicher, wie effizient die Verwendung der AsEnumerable-Methode ist, aber sie ist ein Vergnügen.
Update: Die AsEnumerable-Funktion sieht ziemlich effizient aus! ( Referenz )
quelle
.AsEnumerable()
. ArrayYourType[]
implementiert bereitsIEnumerable<YourType>
. Meine Frage bezog sich jedoch auf den Fall, dass nur die zweite Methode (in Ihrem Beispiel) verfügbar ist und Sie .NET 2.0 verwenden und ein einzelnes Element übergeben möchten.IEnumerable<T> MakeEnumerable<T>(params T[] items) {return items;}
Methode zu definieren? Man könnte das dann mit allem verwenden, was man erwartetIEnumerable<T>
, für eine beliebige Anzahl von diskreten Gegenständen. Der Code sollte im Wesentlichen so effizient sein wie das Definieren einer speziellen Klasse, um ein einzelnes Element zurückzugeben.Obwohl es für eine Methode übertrieben ist, glaube ich, dass einige Leute die interaktiven Erweiterungen nützlich finden könnten.
Die Interactive Extensions (Ix) von Microsoft enthalten die folgende Methode.
Welches kann so verwendet werden:
Ix fügt neue Funktionen hinzu, die in den ursprünglichen Linq-Erweiterungsmethoden nicht enthalten sind, und ist ein direktes Ergebnis der Erstellung der Reactive Extensions (Rx).
Denken Sie,
Linq Extension Methods
+Ix
=Rx
fürIEnumerable
.Sie können sowohl Rx als auch Ix auf CodePlex finden .
quelle
Dies ist 30% schneller als
yield
oderEnumerable.Repeat
bei Verwendungforeach
aufgrund dieser C # -Compileroptimierung und in anderen Fällen von gleicher Leistung.in diesem Test:
quelle
new [] { i }
Option ist etwa 3,2-mal schneller als Ihre Option. Auch Ihre Option ist etwa 1,2-mal schneller als die Erweiterung mityield return
.SingleEnumerator GetEnumerator()
, die Ihre Struktur zurückgibt. Der C # -Compiler verwendet diese Methode (er sucht sie über die Ententypisierung) anstelle der Schnittstellenmethoden und vermeidet so das Boxen. Viele integrierte Sammlungen verwenden diesen Trick, wie z . B. List . Hinweis: Dies funktioniert nur, wenn SieSingleSequence
wie in Ihrem Beispiel direkt darauf verweisen . Wenn Sie es in einerIEnumerable<>
Variablen speichern, funktioniert der Trick nicht (die Schnittstellenmethode wird verwendet)IanG hat einen guten Beitrag zu diesem Thema , der
EnumerableFrom()
als Namen vorgeschlagen wird (und erwähnt, dass Haskell und Rx ihn nennenReturn
).IIRC F # nennt es auch Return. F #Seq
ruft den Operator ansingleton<'T>
.Wenn Sie bereit sind, C # -zentriert zu sein, ist es verlockend, es zu nennen
Yield
[was auf dieyield return
an der Realisierung Beteiligten anspielt ].Wenn Sie sich für die Perfektion interessieren, hat James Michael Hare auch einen Post mit null oder einem Artikel, der einen Scan wert ist.
quelle
Das ist vielleicht nicht besser, aber irgendwie cool:
quelle
Ich stimme den Kommentaren von @ EarthEngine zum ursprünglichen Beitrag zu, wonach 'AsSingleton' ein besserer Name ist. Siehe diesen Wikipedia-Eintrag . Aus der Definition von Singleton folgt dann, dass 'AsSingleton', wenn ein Nullwert als Argument übergeben wird, eine IEnumerable mit einem einzelnen Nullwert anstelle einer leeren IEnumerable zurückgeben sollte, die die
if (item == null) yield break;
Debatte regeln würde . Ich denke, die beste Lösung besteht darin, zwei Methoden zu haben: 'AsSingleton' und 'AsSingletonOrEmpty'; In dem Fall, dass eine Null als Argument übergeben wird, gibt 'AsSingleton' einen einzelnen Nullwert und 'AsSingletonOrEmpty' eine leere IEnumerable zurück. So was:Dann wären diese mehr oder weniger analog zu den Erweiterungsmethoden 'First' und 'FirstOrDefault' in IEnumerable, was sich einfach richtig anfühlt.
quelle
Der einfachste Weg, den ich sagen würde, wäre
new T[]{item};
; Dafür gibt es keine Syntax. Das nächste Äquivalent, an das ich denken kann, ist dasparams
Schlüsselwort, aber das erfordert natürlich Zugriff auf die Methodendefinition und kann nur mit Arrays verwendet werden.quelle
Der obige Code ist nützlich, wenn Sie like verwenden
Im obigen Code-Snippet gibt es keine Leer- / Null-Prüfung, und es wird garantiert, dass nur ein Objekt ohne Angst vor Ausnahmen zurückgegeben wird. Da es faul ist, wird der Abschluss nicht ausgeführt, bis nachgewiesen wird, dass keine Daten vorhanden sind, die den Kriterien entsprechen.
quelle
MyData.Where(myCondition)
leer ist, ist dies bereits möglich (und einfacher) mitDefaultIfEmpty()
:var existingOrNewObject = MyData.Where(myCondition).DefaultIfEmpty(defaultValue).First();
. Dies kann weiter vereinfacht werden,var existingOrNewObject = MyData.FirstOrDefault(myCondition);
wenn Sie möchten,default(T)
und nicht um einen benutzerdefinierten Wert.Ich bin etwas spät zur Party, aber ich werde mich trotzdem teilen. Mein Problem war, dass ich die ItemSource oder eine WPF TreeView an ein einzelnes Objekt binden wollte. Die Hierarchie sieht folgendermaßen aus:
Projekt> Grundstück (e)> Raum (e)
Es gab immer nur ein Projekt, aber ich wollte das Projekt trotzdem im Baum anzeigen, ohne eine Sammlung mit nur diesem einen Objekt übergeben zu müssen, wie von einigen vorgeschlagen.
Da Sie nur IEnumerable-Objekte als ItemSource übergeben können, habe ich beschlossen, meine Klasse IEnumerable zu machen:
Und erstelle meinen eigenen Enumerator entsprechend:
Dies ist wahrscheinlich nicht die "sauberste" Lösung, aber es hat bei mir funktioniert.
BEARBEITEN
Um das Prinzip der Einzelverantwortung aufrechtzuerhalten, wie @Groo hervorhob, habe ich eine neue Wrapper-Klasse erstellt:
Was ich so benutzt habe
EDIT 2
Ich habe einen Fehler mit der
MoveNext()
Methode korrigiert .quelle
SingleItemEnumerator<T>
Klasse macht Sinn, aber eine Klasse zu einem "EinzelstückIEnumerable
von sich selbst" zu machen, scheint eine Verletzung des Prinzips der Einzelverantwortung zu sein. Vielleicht macht es das Weitergeben praktischer, aber ich würde es trotzdem vorziehen, es nach Bedarf einzuwickeln.Um unter "Nicht unbedingt eine gute Lösung, aber dennoch ... eine Lösung" oder "Dumme LINQ-Tricks" abgelegt zu werden , können Sie
Enumerable.Empty<>()
mitEnumerable.Append<>()
...... oder
Enumerable.Prepend<>()
...Die beiden letztgenannten Methoden sind seit .NET Framework 4.7.1 und .NET Core 1.0 verfügbar.
Dies ist eine praktikable Lösung, wenn man wirklich beabsichtigte, vorhandene Methoden zu verwenden, anstatt eigene zu schreiben, obwohl ich mich nicht sicher bin, ob dies mehr oder weniger klar ist als die
Enumerable.Repeat<>()
Lösung . Dies ist definitiv längerer Code (teilweise aufgrund der nicht möglichen Inferenz von TypparameternEmpty<>()
) und erstellt jedoch doppelt so viele Enumerator-Objekte.Abgerundet wird dies durch "Wussten Sie, dass diese Methoden existieren?" Antwort,
Array.Empty<>()
könnte ersetzt werdenEnumerable.Empty<>()
, aber es ist schwer zu argumentieren, dass die Situation ... besser macht.quelle
Ich habe kürzlich dasselbe in einem anderen Beitrag gefragt ( Gibt es eine Möglichkeit, eine C # -Methode aufzurufen, für die ein IEnumerable <T> mit einem einzelnen Wert erforderlich ist? ... mit Benchmarking ).
Ich wollte, dass die Leute hier vorbeischauen, um den kurzen Benchmark-Vergleich zu sehen, der in diesem neueren Beitrag für 4 der in diesen Antworten vorgestellten Ansätze gezeigt wird.
Es scheint, dass das einfache Schreiben
new[] { x }
der Argumente zur Methode die kürzeste und schnellste Lösung ist.quelle