Przekazywanie pojedynczego elementu jako liczby

Czy istnieje wspólny sposób przekazywania pojedynczego elementu typu T do metody, która oczekuje parametru IEnumerable<T>? Językiem jest C#, framework w wersji 2.0.

Obecnie używam metody pomocniczej (jest to. Net 2.0, więc mam całą masę metod pomocniczych do rzutowania / rzutowania podobnych do LINQ), ale to wydaje się głupie: {]}

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

Innym sposobem byłoby oczywiście utworzenie i wypełnienie List<T> lub Array i przekazanie go zamiast IEnumerable<T>.

[Edit] jako metoda rozszerzenia można go nazwać:

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

Czy coś mi umknęło?

[Edit2] uznaliśmy, że someObject.Yield() (jak zasugerował @Peter w komentarzach poniżej) jest najlepszą nazwą dla tej metody rozszerzenia, głównie dla zwięzłości, więc tutaj jest wraz z komentarzem XML, jeśli ktoś chce go złapać:

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;
    }
}
Author: Groo, 2009-10-16

15 answers

Twoja metoda pomocnicza jest najczystszym sposobem, IMO. Jeśli podasz listę lub tablicę, wtedy pozbawiony skrupułów fragment kodu może go rzucić I zmienić zawartość, prowadząc do dziwnego zachowania w niektórych sytuacjach. Możesz użyć kolekcji tylko do odczytu, ale prawdopodobnie będzie to wymagało jeszcze więcej owijania. Myślę, że Twoje rozwiązanie jest tak schludne, jak tylko się da.

 81
Author: Jon Skeet,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-10-16 12:49:05

Cóż, jeśli metoda oczekuje IEnumerable musisz przekazać coś, co jest listą, nawet jeśli zawiera tylko jeden element.

Przechodzenie

new T[] { item }

Jako, że argument powinien wystarczyć myślę

 101
Author: Mario F,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-10-16 12:45:19

W C# 3.0 można korzystać z systemu.Linq.Klasa "Enumerable": {]}

// using System.Linq

Enumerable.Repeat(item, 1);

Spowoduje to utworzenie nowej liczby IEnumerable, która zawiera tylko twój przedmiot.

 93
Author: luksan,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-12-22 19:36:24

W C# 3 (wiem, że powiedziałeś 2), możesz napisać generyczną metodę rozszerzenia, która może sprawić, że składnia będzie trochę bardziej akceptowalna:

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

Kod Klienta to item.ToEnumerable().

 25
Author: Ðаn,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-12-19 19:58:45

Jestem trochę zaskoczony, że nikt nie zasugerował nowego przeciążenia metody argumentem typu T w celu uproszczenia interfejsu API klienta.

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

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

Teraz Twój kod klienta może po prostu to zrobić:

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

Lub z listą:

List<MyItem> itemList = new List<MyItem>();
Obj.DoSomething(itemList);
 9
Author: Joshua Starner,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-11-05 16:15:53

Ta metoda pomocnicza działa dla item lub many.

public static IEnumerable<T> ToEnumerable<T>(params T[] items)
{
    return items;
}    
 8
Author: duraid,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-05-01 13:51:07

Jak właśnie znalazłem, i widziałem, że użytkownik LukeH zasugerował też, miły prosty sposób na zrobienie tego jest następujący:

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

Ten wzorzec pozwoli Ci wywołać tę samą funkcjonalność na wiele sposobów: pojedynczy element; wiele elementów( oddzielonych przecinkami); tablica; lista; wyliczenie itp.

Nie jestem w 100% pewien skuteczności stosowania metody ilościowej, ale to działa.

Update: Theenumerable function looks pretty efficient! (reference )

 7
Author: teppicymon,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 12:18:01

Albo (jak wcześniej powiedziano)

MyMethodThatExpectsAnIEnumerable(new[] { myObject });

Lub

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

Na marginesie, ostatnia wersja może być również przyjemna, jeśli chcesz mieć pustą listę anonimowego obiektu, np.

var x = MyMethodThatExpectsAnIEnumerable(Enumerable.Repeat(new { a = 0, b = "x" }, 0));
 5
Author: erikkallen,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-10-16 13:22:21

To może nie jest lepsze ale jest fajne:

Enumerable.Range(0, 1).Select(i => item);
 4
Author: Ken Lange,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-09-23 13:49:42

Jest to o 30% szybsze niż yield lub Enumerable.Repeat w przypadku użycia w foreach ze względu na tę optymalizację kompilatora C# , a w innych przypadkach o tej samej wydajności.

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

W tym teście:

    // 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}");
    }
 4
Author: V.B.,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 11:47:23

Chociaż jest to przesada dla jednej metody, wierzę, że niektórzy ludzie mogą uznać interaktywne rozszerzenia za przydatne.

Interactive Extensions (Ix) firmy Microsoft zawiera następującą metodę.

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

Które można wykorzystać w ten sposób:

var result = EnumerableEx.Return(0);

Ix dodaje nową funkcjonalność, której nie można znaleźć w oryginalnych metodach rozszerzenia Linq, i jest bezpośrednim wynikiem tworzenia Reactive Extensions (Rx).

Myśl, Linq Extension Methods + Ix = Rx dla IEnumerable.

Możesz znaleźć zarówno Rx i Ix na CodePlex .

 3
Author: cwharris,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-11-25 23:00:19

Zgadzam się z komentarzami @ EarthEngine do oryginalnego posta, czyli "AsSingleton" to lepsza nazwa. Zobacz ten wpis w Wikipedii . Następnie z definicji Singletona wynika, że jeśli wartość null zostanie przekazana jako argument, że 'AsSingleton' powinien zwrócić IEnumerable z pojedynczą wartością null zamiast pustego IEnumerable, który rozstrzygnie debatę if (item == null) yield break;. Myślę, że najlepszym rozwiązaniem jest posiadanie dwóch metod: "AsSingleton" i "AsSingletonOrEmpty"; gdzie w przypadku jeśli wartość null zostanie przekazana jako argument, 'AsSingleton' zwróci pojedynczą wartość null, a 'AsSingletonOrEmpty' zwróci pustą liczbę. Tak:

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

Wtedy, te, mniej lub bardziej, być analogiczny do 'pierwszy' i 'FirstOrDefault' metody rozszerzenia na IEnumerable, który po prostu czuje się dobrze.

 3
Author: Jason Boyd,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-04-10 16:35:15

IanG mA dobry post na temat , sugerując EnumerableFrom() jako nazwę i wspomina, że dyskusja wskazuje, że Haskell i Rx nazywają to Return. IIRC f # nazywa go również Return . F # ' SSeq dzwoni do operatora singleton<'T>.

Kuszące, jeśli jesteś przygotowany na C#-centric jest nazwać to Yield [nawiązując do yield return zaangażowany w realizację tego].

Jeśli interesują Cię aspekty perf, James Michael Hare ma zwracający zero lub jeden pozycje post zbyt, który jest wart skanowania.

 2
Author: Ruben Bartelink,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-02-19 12:33:17

Najłatwiej byłoby powiedzieć new T[]{item};; nie ma składni, aby to zrobić. Najbliższym odpowiednikiem, jaki przychodzi mi do głowy, jest słowo kluczowe params, ale oczywiście wymaga to dostępu do definicji metody i może być użyte tylko z tablicami.

 1
Author: FacticiusVir,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-10-16 12:48:56

Jestem trochę spóźniona na imprezę, ale i tak podzielę się swoją drogą. Mój problem polegał na tym, że chciałem powiązać ItemSource lub widok drzewa WPF z jednym obiektem. Hierarchia wygląda tak:

Projekt > działka(y) > Pokój(y)

Zawsze miał być tylko jeden projekt, ale nadal chciałem pokazać projekt w drzewie, bez konieczności przekazywania kolekcji z tylko tym jednym obiektem, jak niektórzy sugerowali.
Ponieważ można przekazać tylko nieliczne przedmioty jako przedmioty, postanowiłem aby moja klasa była liczniejsza:

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

    ... 

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

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

I odpowiednio utworzyć własny Enumerator:

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

To prawdopodobnie nie jest" najczystsze " rozwiązanie, ale działało dla mnie.

EDIT
Aby podtrzymać zasadę pojedynczej odpowiedzialności jak zauważył @Groo, stworzyłem nową klasę wrapper:

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

Które użyłem w ten sposób

TreeView.ItemSource = new SingleItemWrapper(itemToWrap);

EDIT 2
Poprawiłem błąd metodą MoveNext().

 0
Author: IDarkCoder,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-08-23 07:30:57