Jak mogę zwrócić pusty IEnumerable?

329

Biorąc pod uwagę następujący kod i sugestie podane w tym pytaniu , postanowiłem zmodyfikować tę oryginalną metodę i zapytać, czy w IEnumarable są jakieś wartości, zwróć ją, jeśli nie, zwróć IEnumerable bez wartości.

Oto metoda:

public IEnumerable<Friend> FindFriends()
        {
            //Many thanks to Rex-M for his help with this one.
            //https://stackoverflow.com/users/67/rex-m

            return doc.Descendants("user").Select(user => new Friend
            {
                ID = user.Element("id").Value,
                Name = user.Element("name").Value,
                URL = user.Element("url").Value,
                Photo = user.Element("photo").Value
            });
        }

Ponieważ wszystko znajduje się w instrukcji return, nie wiem, jak to zrobić. Czy coś takiego mogłoby działać?

public IEnumerable<Friend> FindFriends()
        {
            //Many thanks to Rex-M for his help with this one.
            //https://stackoverflow.com/users/67/rex-m
            if (userExists)
            {
                return doc.Descendants("user").Select(user => new Friend
                {
                    ID = user.Element("id").Value,
                    Name = user.Element("name").Value,
                    URL = user.Element("url").Value,
                    Photo = user.Element("photo").Value
                });
            }
            else
            { 
                return new IEnumerable<Friend>();
            }
        }

Powyższa metoda nie działa, a właściwie nie powinna; Po prostu czuję, że to ilustruje moje intencje. Wydaje mi się, że powinienem określić, że kod nie działa, ponieważ nie można utworzyć instancji klasy abstrakcyjnej.

Oto kod wywołujący, nie chcę, aby w dowolnym momencie odbierał pusty IEnumerable:

private void SetUserFriends(IEnumerable<Friend> list)
        {
            int x = 40;
            int y = 3;


            foreach (Friend friend in list)
            {
                FriendControl control = new FriendControl();
                control.ID = friend.ID;
                control.URL = friend.URL;
                control.SetID(friend.ID);
                control.SetName(friend.Name);
                control.SetImage(friend.Photo);

                control.Location = new Point(x, y);
                panel2.Controls.Add(control);

                y = y + control.Height + 4;
            } 

        }

Dziękuję za Twój czas.

Sergio Tapia
źródło
2
Patrząc na kod tutaj powinieneś używać zwrotu z zysku i podziału zysku.
Chris Marisic,

Odpowiedzi:

575

Możesz użyć list ?? Enumerable.Empty<Friend>()lub otrzymać FindFriendszwrotEnumerable.Empty<Friend>()

Michał Mrożek
źródło
7
Czy zmieniłoby to wszystko, gdyby wrócił, powiedzmy, new List<Friend>()skoro zostanie ono IEnumerable<Friend>przywrócone po powrocie z tej metody?
Sarah Vessels
73
new List<Friend>()jest droższą operacją, ponieważ stworzyłby instancję listy (i przydzieliłby dla niej pamięć)
Igor Pashchuk
105

Jak dla mnie, najbardziej elegancki jest sposób yield break

Pavel Tupitsyn
źródło
8
Ale tak jest, jeśli użyjesz zwrotu z zysku i tak nie jest, prawda?
Svish,
15
+1, ponieważ jego kod poprawnie powinien używać plonu do sposobu, w jaki pracuje z IEnumerable
Chris Marisic
6
Przepraszam za moją niewiedzę na ten temat, ale czy mógłbyś zilustrować sposób wykorzystania podziału zysku w tym kontekście? Widziałem przykłady tylko dla pętli, ale to nie maluje dla mnie wyraźnego obrazu.
Sergio Tapia,
Zaktualizowałem odpowiedź o przykład. Zgadzam się, że to najbardziej elegancki sposób na zrobienie tego. :)
Johny Skovdal,
4
Edycja została odrzucona w recenzji, więc oto przykład, o którym mówiłem o @Pyritie - formatowanie jest jednak pomieszane, więc dodałem go również do pastebin.com/X9Z49Vq1 :public IEnumerable<Friend> FindFriends() { if(!userExists) yield break; foreach(var descendant in doc.Descendants("user").Select(user => new Friend { ID = user.Element("id").Value, Name = user.Element("name").Value, URL = user.Element("url").Value, Photo = user.Element("photo").Value })) { yield return descendant; } }
Johny Skovdal
8

To oczywiście tylko kwestia osobistych preferencji, ale napisałbym tę funkcję, używając zwrotu z zysku:

public IEnumerable<Friend> FindFriends()
{
    //Many thanks to Rex-M for his help with this one.
    //http://stackoverflow.com/users/67/rex-m
    if (userExists)
    {
        foreach(var user in doc.Descendants("user"))
        {
            yield return new Friend
                {
                    ID = user.Element("id").Value,
                    Name = user.Element("name").Value,
                    URL = user.Element("url").Value,
                    Photo = user.Element("photo").Value
                }
        }
    }
}
Chaos
źródło
1

Myślę, że najprostszy sposób byłby

 return new Friend[0];

Wymagania dotyczące zwrotu polegają jedynie na tym, że metoda zwraca obiekt, który implementuje IEnumerable<Friend>. Fakt, że w różnych okolicznościach zwracane są dwa różne rodzaje obiektów, jest nieistotny, o ile oba implementują IEnumerable.

James Curran
źródło
5
Enumerable.Empty <T> faktycznie zwraca pustą tablicę T (T [0]), z tą zaletą, że ta sama pusta tablica jest ponownie używana. Zauważ, że to podejście nie jest idealne dla niepustych tablic, ponieważ elementy można modyfikować (jednak nie można zmienić rozmiaru tablicy, zmiana rozmiaru wymaga utworzenia nowej instancji).
Francis Gagné
0
public IEnumerable<Friend> FindFriends()
{
    return userExists ? doc.Descendants("user").Select(user => new Friend
        {
            ID = user.Element("id").Value,
            Name = user.Element("name").Value,
            URL = user.Element("url").Value,
            Photo = user.Element("photo").Value
        }): new List<Friend>();
}
Natarajan Ganapathi
źródło