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.


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


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


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

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.


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>();
}
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.