472 Stimmen

Übergabe eines einzelnen Elements als IEnumerable<T>

Gibt es eine gängige Methode, um ein einzelnes Element vom Typ T zu einer Methode, die eine IEnumerable<T> Parameter? Sprache ist C#, Framework Version 2.0.

Derzeit verwende ich eine Hilfsmethode (es ist .Net 2.0, so habe ich eine ganze Reihe von Casting/Projecting-Hilfsmethoden ähnlich wie LINQ), aber dies scheint einfach dumm:

public static class IEnumerableExt
{
    // usage: IEnumerableExt.FromSingleItem(someObject);
    public static IEnumerable<T> FromSingleItem<T>(T item)
    {
        yield return item; 
    }
}

Eine andere Möglichkeit wäre natürlich die Erstellung und Befüllung einer List<T> oder ein Array und übergeben Sie es anstelle von IEnumerable<T> .

[Bearbeiten] Als Erweiterungsmethode könnte sie benannt werden:

public static class IEnumerableExt
{
    // usage: someObject.SingleItemAsEnumerable();
    public static IEnumerable<T> SingleItemAsEnumerable<T>(this T item)
    {
        yield return item; 
    }
}

Übersehe ich hier etwas?

[Bearbeiten2] Wir fanden someObject.Yield() (wie @Peter in den Kommentaren unten vorschlug) für den besten Namen für diese Erweiterungsmethode, vor allem der Kürze halber, also hier ist er zusammen mit dem XML-Kommentar, falls jemand ihn verwenden 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;
    }
}

13voto

Joshua Starner Punkte 1555

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 genau dies tun:

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

oder mit einer Liste:

List<MyItem> itemList = new List<MyItem>();
Obj.DoSomething(itemList);

10voto

V.B. Punkte 6034

Dies ist 30 % schneller als yield ou Enumerable.Repeat bei Verwendung in foreach aufgrund von diese C#-Compiler-Optimierung und in anderen Fällen die gleiche 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}");
    }

9voto

Jason Boyd Punkte 6439

Ich stimme mit @EarthEngine's Kommentar zum ursprünglichen Beitrag überein, der besagt, dass "AsSingleton" ein besserer Name ist. Siehe diesen Eintrag in der Wikipedia . Dann folgt aus der Definition von Singleton, dass, wenn ein Nullwert als Argument übergeben wird, "AsSingleton" eine IEnumerable mit einem einzigen Nullwert zurückgeben sollte, anstatt einer leeren IEnumerable, die die if (item == null) yield break; Debatte. Ich denke, die beste Lösung ist es, zwei Methoden zu haben: 'AsSingleton' und 'AsSingletonOrEmpty'; wobei 'AsSingleton' im Falle der Übergabe einer Null als Argument einen einzelnen Nullwert zurückgibt und 'AsSingletonOrEmpty' einen leeren IEnumerable zurückgibt. Etwa so:

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" für IEnumerable, was sich einfach richtig anfühlt.

8voto

erikkallen Punkte 32657

Entweder (wie bereits gesagt wurde)

MyMethodThatExpectsAnIEnumerable(new[] { myObject });

または

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

Nebenbei bemerkt, kann die letzte Version auch nützlich sein, wenn man eine leere Liste eines anonymen Objekts haben möchte, z.B.

var x = MyMethodThatExpectsAnIEnumerable(Enumerable.Repeat(new { a = 0, b = "x" }, 0));

7voto

teppicymon Punkte 71

Wie ich soeben herausgefunden und gesehen habe, dass der Benutzer LukeH ebenfalls einen Vorschlag gemacht 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 die gleiche Funktionalität auf vielfältige Weise aufrufen: ein einzelnes Element, mehrere Elemente (durch Komma getrennt), ein Array, eine Liste, eine Aufzählung usw.

Ich bin nicht 100 % sicher, auf die Effizienz der Verwendung der AsEnumerable-Methode zwar, aber es funktioniert eine Behandlung.

Update: Die AsEnumerable-Funktion sieht ziemlich effizient aus! ( Referenz )

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X