Übergeben eines einzelnen Elements als IEnumerable <T>

377

Gibt es eine übliche Möglichkeit, ein einzelnes Element vom Typ Tan 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 Arrayund 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&lt;T&gt;
    /// 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&lt;T&gt; consisting of a single item. </returns>
    public static IEnumerable<T> Yield<T>(this T item)
    {
        yield return item;
    }
}
Groo
quelle
6
Ich würde eine geringfügige Änderung im Hauptteil der Erweiterungsmethode vornehmen: if (item == null) yield break; Jetzt können Sie keine Null mehr übergeben und das (triviale) Null-Objektmuster für nutzen IEnumerable. ( foreach (var x in xs)behandelt eine leere xsganz gut). Übrigens ist diese Funktion die monadische Einheit für die Listenmonade IEnumerable<T>, und angesichts des Monaden-Liebesfestes bei Microsoft bin ich überrascht, dass so etwas überhaupt nicht im Framework enthalten ist.
Matt Enright
2
Für die Erweiterungsmethode sollten Sie sie nicht benennen, AsEnumerableda bereits eine integrierte Erweiterung mit diesem Namen vorhanden ist . (Wenn Timplementiert IEnumerable, z string. B. )
Jon-Eric
22
Wie wäre es mit der Benennung der Methode Yield? Nichts geht über die Kürze.
Philip Guin
4
Namensvorschläge hier. "SingleItemAsEnumerable" etwas ausführlich. "Yield" beschreibt eher die Implementierung als die Schnittstelle - was nicht gut ist. Für einen besseren Namen schlage ich "AsSingleton" vor, die der genauen Bedeutung des Verhaltens entsprechen.
Earth Engine
4
Ich hasse den left==nullScheck 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 wie new T[] {}, und eines Tages müssen Sie sie möglicherweise unterscheiden.
Earth Engine

Antworten:

100

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.

Jon Skeet
quelle
Nun, wenn die Liste / das Array ad-hoc erstellt wird, endet ihr Gültigkeitsbereich nach dem Methodenaufruf, sodass es keine Probleme verursachen sollte
Mario F
Wie kann es geändert werden, wenn es als Array gesendet wird? Ich denke, es könnte in ein Array umgewandelt werden und dann den Verweis auf etwas anderes ändern, aber was würde das nützen? (Ich vermisse wahrscheinlich etwas ...)
Svish
2
Angenommen, Sie haben beschlossen, eine Aufzählung zu erstellen, um sie an zwei verschiedene Methoden zu übergeben. Dann hat die erste sie in ein Array umgewandelt und den Inhalt geändert. Sie übergeben es dann als Argument an eine andere Methode, ohne die Änderung zu kennen. Ich sage nicht, dass es wahrscheinlich ist, nur dass es weniger ordentlich ist, etwas Veränderliches zu haben, wenn es nicht sein muss, als die natürliche Unveränderlichkeit.
Jon Skeet
3
Dies ist eine akzeptierte Antwort, die höchstwahrscheinlich gelesen wird. Daher werde ich hier meine Besorgnis hinzufügen. Ich habe versucht , diese Methode, aber das brach mir das vorher kompilieren Aufruf myDict.Keys.AsEnumerable()wo myDictwar der Typ Dictionary<CheckBox, Tuple<int, int>>. Nachdem ich die Erweiterungsmethode in umbenannt hatte SingleItemAsEnumerable, 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?
Hamish Grubijan
3
@ HamishGrubijan: Es hört sich so an, als wollten Sie erst einmal dictionary.Valuesdamit anfangen. Grundsätzlich ist nicht klar, was los ist, aber ich schlage vor, Sie beginnen eine neue Frage darüber.
Jon Skeet
133

Nun, wenn die Methode erwartet, dass IEnumerableSie etwas übergeben müssen, das eine Liste ist, auch wenn es nur ein Element enthält.

Vorbeigehen

new[] { item }

da sollte das argument ausreichen sollte ich denke

Mario F.
quelle
32
Ich denke, Sie könnten sogar das T fallen lassen, da es abgeleitet wird (es sei denn, ich irre mich hier sehr: p)
Svish
2
Ich denke, dass es in C # 2.0 nicht abgeleitet wird.
Groo
4
"Sprache ist C #, Framework Version 2" Der C # 3-Compiler kann auf T schließen, und der Code ist vollständig kompatibel mit .NET 2.
Sisve
beste Antwort ist unten?!
Falco Alexander
2
@Svish Ich schlug vor und bearbeitete die Antwort, um genau das zu tun - wir sind jetzt im Jahr 2020, also sollte es der "Standard" imho sein
OschtärEi
120

In C # 3.0 können Sie die System.Linq.Enumerable-Klasse verwenden:

// using System.Linq

Enumerable.Repeat(item, 1);

Dadurch wird eine neue IEnumerable erstellt, die nur Ihren Artikel enthält.

luksan
quelle
26
Ich denke, dass diese Lösung das Lesen des Codes erschwert. :( Wiederholung auf einem einzelnen Element ist ziemlich kontraintuitiv, finden Sie nicht?
Drahakar
6
Wiederholen Sie einmal liest ok für mich. Weitaus einfachere Lösung, ohne sich Gedanken über das Hinzufügen einer weiteren Erweiterungsmethode machen zu müssen und sicherzustellen, dass der Namespace überall dort enthalten ist, wo Sie ihn verwenden möchten.
Si618
13
Ja, ich benutze new[]{ item }mich eigentlich nur selbst, ich dachte nur, das wäre eine interessante Lösung.
Luksan
7
Diese Lösung ist Geld.
ProVega
IMHO sollte dies die akzeptierte Antwort sein. Es ist einfach zu lesen, übersichtlich und in einer einzelnen Zeile in der BCL implementiert, ohne eine benutzerdefinierte Erweiterungsmethode.
Ryan
35

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:

static class IEnumerableExtensions
{
    public static IEnumerable<T> ToEnumerable<T>(this T item)
    {
        yield return item;
    }
}

Client-Code ist dann item.ToEnumerable().

Nаn
quelle
Vielen Dank, ich bin mir dessen bewusst (funktioniert in .NET 2.0, wenn ich C # 3.0 verwende). Ich habe mich nur gefragt, ob es dafür einen eingebauten Mechanismus gibt.
Groo
12
Dies ist eine wirklich schöne Alternative. Ich möchte nur vorschlagen, ToEnumerableSystem.Linq.Enumerable.AsEnumerable
Fede
3
Als Randnotiz gibt es bereits eine ToEnumerableErweiterungsmethode für IObservable<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.
Cwharris
14

Diese Hilfsmethode funktioniert für Elemente oder viele.

public static IEnumerable<T> ToEnumerable<T>(params T[] items)
{
    return items;
}    
Duraid
quelle
1
Nützliche Variation, da es mehr als ein Element unterstützt. Ich mag es, dass es darauf ankommt params, das Array zu erstellen, so dass der resultierende Code sauber aussieht. Ich habe nicht entschieden, ob es mir mehr oder weniger gefällt als new T[]{ item1, item2, item3 };für mehrere Artikel.
ToolmakerSteve
Clever !
Ich
10

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.

public void DoSomething<T>(IEnumerable<T> list)
{
    // Do Something
}

public void DoSomething<T>(T item)
{
    DoSomething(new T[] { item });
}

Jetzt kann Ihr Client-Code Folgendes tun:

MyItem item = new MyItem();
Obj.DoSomething(item);

oder mit einer Liste:

List<MyItem> itemList = new List<MyItem>();
Obj.DoSomething(itemList);
Joshua Starner
quelle
19
Noch besser, Sie könnten haben, 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.)
LukeH
7

Entweder (wie bereits gesagt)

MyMethodThatExpectsAnIEnumerable(new[] { myObject });

oder

MyMethodThatExpectsAnIEnumerable(Enumerable.Repeat(myObject, 1));

Als Randnotiz kann die letzte Version auch schön sein, wenn Sie eine leere Liste eines anonymen Objekts möchten, z

var x = MyMethodThatExpectsAnIEnumerable(Enumerable.Repeat(new { a = 0, b = "x" }, 0));
erikkallen
quelle
Vielen Dank, obwohl Enumerable.Repeat neu in .Net 3.5 ist. Es scheint sich ähnlich wie die oben beschriebene Hilfsmethode zu verhalten.
Groo
7

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:

public static void PerformAction(params YourType[] items)
{
    // Forward call to IEnumerable overload
    PerformAction(items.AsEnumerable());
}

public static void PerformAction(IEnumerable<YourType> items)
{
    foreach (YourType item in items)
    {
        // Do stuff
    }
}

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 )

Teppicymon
quelle
Eigentlich brauchst du gar nicht .AsEnumerable(). Array YourType[]implementiert bereits IEnumerable<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.
Groo
2
Ja, aber Sie werden feststellen, dass Sie möglicherweise einen Stapelüberlauf bekommen
;-)
Und um Ihre eigentliche Frage zu beantworten (nicht die, die ich eigentlich für mich selbst beantworten wollte!), Müssen Sie sie so ziemlich in ein Array gemäß Marios Antwort
konvertieren
2
Wie wäre es einfach eine IEnumerable<T> MakeEnumerable<T>(params T[] items) {return items;}Methode zu definieren? Man könnte das dann mit allem verwenden, was man erwartet IEnumerable<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.
Supercat
6

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.

public static IEnumerable<TResult> Return<TResult>(TResult value)
{
    yield return value;
}

Welches kann so verwendet werden:

var result = EnumerableEx.Return(0);

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= Rxfür IEnumerable.

Sie können sowohl Rx als auch Ix auf CodePlex finden .

cwharris
quelle
5

Dies ist 30% schneller als yieldoder Enumerable.Repeatbei Verwendung foreachaufgrund dieser C # -Compileroptimierung und in anderen Fällen von gleicher Leistung.

public struct SingleSequence<T> : IEnumerable<T> {
    public struct SingleEnumerator : IEnumerator<T> {
        private readonly SingleSequence<T> _parent;
        private bool _couldMove;
        public SingleEnumerator(ref SingleSequence<T> parent) {
            _parent = parent;
            _couldMove = true;
        }
        public T Current => _parent._value;
        object IEnumerator.Current => Current;
        public void Dispose() { }

        public bool MoveNext() {
            if (!_couldMove) return false;
            _couldMove = false;
            return true;
        }
        public void Reset() {
            _couldMove = true;
        }
    }
    private readonly T _value;
    public SingleSequence(T value) {
        _value = value;
    }
    public IEnumerator<T> GetEnumerator() {
        return new SingleEnumerator(ref this);
    }
    IEnumerator IEnumerable.GetEnumerator() {
        return new SingleEnumerator(ref this);
    }
}

in diesem Test:

    // Fastest among seqs, but still 30x times slower than direct sum
    // 49 mops vs 37 mops for yield, or c.30% faster
    [Test]
    public void SingleSequenceStructForEach() {
        var sw = new Stopwatch();
        sw.Start();
        long sum = 0;
        for (var i = 0; i < 100000000; i++) {
            foreach (var single in new SingleSequence<int>(i)) {
                sum += single;
            }
        }
        sw.Stop();
        Console.WriteLine($"Elapsed {sw.ElapsedMilliseconds}");
        Console.WriteLine($"Mops {100000.0 / sw.ElapsedMilliseconds * 1.0}");
    }
VB
quelle
Vielen Dank, es ist sinnvoll, eine Struktur für diesen Fall zu erstellen, um die GC-Belastung in engen Schleifen zu reduzieren.
Groo
1
Sowohl der Enumerator als auch der Enumerable werden bei der Rückgabe verpackt ...
Stephen Drew
2
Vergleichen Sie nicht mit Stoppuhr. Verwenden Sie Benchmark.NET github.com/dotnet/BenchmarkDotNet
NN_
1
Nur gemessen und new [] { i }Option ist etwa 3,2-mal schneller als Ihre Option. Auch Ihre Option ist etwa 1,2-mal schneller als die Erweiterung mit yield return.
Loror
Sie können dies beschleunigen, indem Sie eine andere C # -Compileroptimierung verwenden: Fügen Sie eine Methode hinzu 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 Sie SingleSequencewie in Ihrem Beispiel direkt darauf verweisen . Wenn Sie es in einer IEnumerable<>Variablen speichern, funktioniert der Trick nicht (die Schnittstellenmethode wird verwendet)
enzi
5

IanG hat einen guten Beitrag zu diesem Thema , der EnumerableFrom()als Namen vorgeschlagen wird (und erwähnt, dass Haskell und Rx ihn nennen Return). IIRC F # nennt es auch Return . F # Seqruft den Operator ansingleton<'T> .

Wenn Sie bereit sind, C # -zentriert zu sein, ist es verlockend, es zu nennen Yield[was auf die yield returnan 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.

Ruben Bartelink
quelle
4

Das ist vielleicht nicht besser, aber irgendwie cool:

Enumerable.Range(0, 1).Select(i => item);
Ken Lange
quelle
Es ist nicht. Es ist anders.
Nawfal
Ewww. Das sind viele Details, nur um aus einem Gegenstand eine Aufzählung zu machen.
ToolmakerSteve
1
Dies entspricht der Verwendung einer Rube Goldberg-Maschine für das Frühstück. Ja, es funktioniert, aber es springt durch 10 Reifen, um dorthin zu gelangen. Eine einfache Sache wie diese kann der Leistungsengpass sein, wenn sie in einer engen Schleife ausgeführt wird, die millionenfach ausgeführt wird. In der Praxis spielt der Leistungsaspekt in 99% der Fälle keine Rolle, aber ich persönlich denke immer noch, dass es ein grob unnötiger Overkill ist.
Enzi
4

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:

public static IEnumerable<T> AsSingletonOrEmpty<T>(this T source)
{
    if (source == null)
    {
        yield break;
    }
    else
    {
        yield return source;
    }
}

public static IEnumerable<T> AsSingleton<T>(this T source)
{
    yield return source;
}

Dann wären diese mehr oder weniger analog zu den Erweiterungsmethoden 'First' und 'FirstOrDefault' in IEnumerable, was sich einfach richtig anfühlt.

Jason Boyd
quelle
2

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 das paramsSchlüsselwort, aber das erfordert natürlich Zugriff auf die Methodendefinition und kann nur mit Arrays verwendet werden.

FacticiusVir
quelle
2
Enumerable.Range(1,1).Select(_ => {
    //Do some stuff... side effects...
    return item;
});

Der obige Code ist nützlich, wenn Sie like verwenden

var existingOrNewObject = MyData.Where(myCondition)
       .Concat(Enumerable.Range(1,1).Select(_ => {
           //Create my object...
           return item;
       })).Take(1).First();

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.

frhack
quelle
Diese Antwort wurde automatisch mit "niedrige Qualität" gekennzeichnet. Wie in der Hilfe erläutert ("Kürze ist akzeptabel, aber ausführlichere Erklärungen sind besser."), Bearbeiten Sie sie, um dem OP mitzuteilen, was er falsch macht und worum es in Ihrem Code geht.
Sandra Rossi
Ich sehe, dass der Großteil dieser Antwort, einschließlich des Teils, auf den ich antworte, später von jemand anderem bearbeitet wurde. Wenn das zweite Snippet jedoch einen Standardwert bereitstellen soll, wenn dieser MyData.Where(myCondition)leer ist, ist dies bereits möglich (und einfacher) mit DefaultIfEmpty(): 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.
Speck
0

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:

public class ProjectClass : IEnumerable<ProjectClass>
{
    private readonly SingleItemEnumerator<AufmassProjekt> enumerator;

    ... 

    public IEnumerator<ProjectClass > GetEnumerator() => this.enumerator;

    IEnumerator IEnumerable.GetEnumerator() => this.GetEnumerator();
}

Und erstelle meinen eigenen Enumerator entsprechend:

public class SingleItemEnumerator : IEnumerator
{
    private bool hasMovedOnce;

    public SingleItemEnumerator(object current)
    {
        this.Current = current;
    }

    public bool MoveNext()
    {
        if (this.hasMovedOnce) return false;
        this.hasMovedOnce = true;
        return true;
    }

    public void Reset()
    { }

    public object Current { get; }
}

public class SingleItemEnumerator<T> : IEnumerator<T>
{
    private bool hasMovedOnce;

    public SingleItemEnumerator(T current)
    {
        this.Current = current;
    }

    public void Dispose() => (this.Current as IDisposable).Dispose();

    public bool MoveNext()
    {
        if (this.hasMovedOnce) return false;
        this.hasMovedOnce = true;
        return true;
    }

    public void Reset()
    { }

    public T Current { get; }

    object IEnumerator.Current => this.Current;
}

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:

public class SingleItemWrapper : IEnumerable
{
    private readonly SingleItemEnumerator enumerator;

    public SingleItemWrapper(object item)
    {
        this.enumerator = new SingleItemEnumerator(item);
    }

    public object Item => this.enumerator.Current;

    public IEnumerator GetEnumerator() => this.enumerator;
}

public class SingleItemWrapper<T> : IEnumerable<T>
{
    private readonly SingleItemEnumerator<T> enumerator;

    public SingleItemWrapper(T item)
    {
        this.enumerator = new SingleItemEnumerator<T>(item);
    }

    public T Item => this.enumerator.Current;

    public IEnumerator<T> GetEnumerator() => this.enumerator;

    IEnumerator IEnumerable.GetEnumerator() => this.GetEnumerator();
}

Was ich so benutzt habe

TreeView.ItemSource = new SingleItemWrapper(itemToWrap);

EDIT 2
Ich habe einen Fehler mit der MoveNext()Methode korrigiert .

IDarkCoder
quelle
Die SingleItemEnumerator<T>Klasse macht Sinn, aber eine Klasse zu einem "Einzelstück IEnumerablevon 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.
Groo
0

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<>()mit Enumerable.Append<>()...

IEnumerable<string> singleElementEnumerable = Enumerable.Empty<string>().Append("Hello, World!");

... oder Enumerable.Prepend<>()...

IEnumerable<string> singleElementEnumerable = Enumerable.Empty<string>().Prepend("Hello, World!");

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 Typparametern Empty<>()) und erstellt jedoch doppelt so viele Enumerator-Objekte.

Abgerundet wird dies durch "Wussten Sie, dass diese Methoden existieren?" Antwort, Array.Empty<>()könnte ersetzt werden Enumerable.Empty<>(), aber es ist schwer zu argumentieren, dass die Situation ... besser macht.

SPECK
quelle
0

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.

Mattica
quelle