¿Cómo puedo devolver un IEnumerable vacío?

329

Dado el siguiente código y las sugerencias dadas en esta pregunta , he decidido modificar este método original y preguntar si hay algún valor en el IEnumarable, devolverlo, si no devolver un IEnumerable sin valores.

Aquí está el método:

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

Como todo está dentro de la declaración de devolución, no sé cómo podría hacer esto. Algo como esto funcionarí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>();
            }
        }

El método anterior no funciona, y de hecho no se supone que lo haga; Solo siento que ilustra mis intenciones. Creo que debería especificar que el código no funciona porque no se puede crear una instancia de una clase abstracta.

Aquí está el código de llamada, no quiero que reciba un IEnumerable nulo en ningún momento:

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

        }

Gracias por tu tiempo.

Sergio Tapia
fuente
2
Mirando el código aquí, debería estar utilizando el rendimiento y la ruptura de rendimiento.
Chris Marisic

Respuestas:

575

Puedes usar list ?? Enumerable.Empty<Friend>(), o tener FindFriendsdevoluciónEnumerable.Empty<Friend>()

Michael Mrozek
fuente
77
¿Cambiaría las cosas si regresara, por ejemplo, new List<Friend>()ya que será lanzado IEnumerable<Friend>cuando regrese de ese método?
Sarah Vessels
73
new List<Friend>()es una operación más costosa porque crearía una instancia de una lista (y asignaría memoria para ello en el proceso)
Igor Pashchuk
106

En cuanto a mí, la forma más elegante es yield break

Pavel Tupitsyn
fuente
8
Pero eso es si usas rendimiento de rendimiento y tal, ¿no?
Svish
15
+1 ya que su código correctamente debería usar el rendimiento por la forma en que trabaja con IEnumerable
Chris Marisic
66
Disculpe mi ignorancia sobre el tema, pero ¿podría ilustrar cómo utilizar el límite de rendimiento en este contexto? He visto ejemplos solo para bucles, pero eso no me da una imagen clara.
Sergio Tapia
Se actualizó la respuesta con un ejemplo. Realmente es la forma más elegante de hacerlo, estaría de acuerdo. :)
Johny Skovdal
44
Editar fue rechazado en la revisión por pares, así que aquí está el ejemplo del que estaba hablando @Pyritie: sin embargo, el formato se estropea, así que también lo agregué a 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

Por supuesto, eso es solo una cuestión de preferencia personal, pero escribiría esta función usando return return:

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
                }
        }
    }
}
Caos
fuente
1

Creo que la forma más sencilla sería

 return new Friend[0];

Los requisitos de la devolución son simplemente que el método devuelve un objeto que implementa IEnumerable<Friend>. El hecho de que, en diferentes circunstancias, devuelva dos tipos diferentes de objetos es irrelevante, siempre que ambos implementen IEnumerable.

James Curran
fuente
55
Enumerable.Empty <T> en realidad devuelve una matriz vacía de T (T [0]), con la ventaja de que se reutiliza la misma matriz vacía. Tenga en cuenta que este enfoque no es ideal para matrices no vacías, porque los elementos se pueden modificar (sin embargo, una matriz no se puede cambiar de tamaño, el cambio de tamaño implica la creación de una nueva instancia).
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
fuente