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;
    }
}

2voto

FacticiusVir Punkte 2017

Der einfachste Weg wäre meiner Meinung nach new T[]{item}; ; es gibt keine Syntax, um dies zu tun. Das nächste Äquivalent, das mir einfällt, ist die params Schlüsselwort, aber das setzt natürlich voraus, dass Sie Zugriff auf die Methodendefinition haben und ist nur mit Arrays verwendbar.

2voto

frhack Punkte 4410
Enumerable.Range(1,1).Select(_ => {
    //Do some stuff... side effects...
    return item;
});

Der obige Code ist nützlich, wenn man wie

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

Im obigen Codeschnipsel gibt es keine Leer/Null-Prüfung, und es wird garantiert, dass nur ein Objekt zurückgegeben wird, ohne dass Ausnahmen zu befürchten sind. Außerdem wird die Schließung erst ausgeführt, wenn bewiesen ist, dass keine Daten vorhanden sind, die den Kriterien entsprechen, da es sich um eine träge Funktion handelt.

1voto

Luke Kubat Punkte 41

Ich bevorzuge

public static IEnumerable<T> Collect<T>(this T item, params T[] otherItems)
{
    yield return item;
    foreach (var otherItem in otherItems)
    {
        yield return otherItem;
    }
}

Damit können Sie Folgendes aufrufen item.Collect() wenn Sie das Singleton wollen, aber es ermöglicht Ihnen auch den Aufruf von item.Collect(item2, item3) wenn Sie möchten

0voto

IDarkCoder Punkte 689

Ich bin zwar ein bisschen spät dran, aber ich werde mich trotzdem beteiligen. Mein Problem war, dass ich die ItemSource oder eine WPF TreeView an ein einzelnes Objekt binden wollte. Die Hierarchie sieht wie folgt aus:

Projekt > Grundstück(e) > Raum(e)

Es sollte immer nur ein Projekt geben, aber ich wollte trotzdem das Projekt in der Struktur anzeigen, ohne eine Sammlung mit nur diesem einen Objekt darin zu übergeben, wie es einige vorgeschlagen haben.
Da man nur IEnumerable-Objekte als ItemSource übergeben kann, 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 meinen eigenen Enumerator entsprechend erstellen:

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;
}

Das ist wahrscheinlich nicht die "sauberste" Lösung, aber bei mir hat sie funktioniert.

EDIT
Zur Aufrechterhaltung der Grundsatz der einzigen Verantwortung Wie @Groo schon sagte, 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();
}

Diese habe ich wie folgt verwendet

TreeView.ItemSource = new SingleItemWrapper(itemToWrap);

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

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