Como posso retornar um IEnumerable vazio?

329

Dado o código a seguir e as sugestões fornecidas nesta pergunta , decidi modificar esse método original e perguntar se há algum valor no IEnumarable retorná-lo, se não retornar um IEnumerable sem valores.

Aqui está o 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 tudo está dentro da declaração de retorno, não sei como poderia fazer isso. Algo assim funcionaria?

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

O método acima não funciona e, de fato, não deveria; Eu apenas sinto que ilustra minhas intenções. Acho que devo especificar que o código não funciona porque você não pode criar uma instância de uma classe abstrata.

Aqui está o código de chamada, não quero que ele receba um IEnumerable nulo a qualquer 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;
            } 

        }

Obrigado pelo seu tempo.

Sergio Tapia
fonte
2
Olhando para o código aqui, você deve estar usando retorno e quebra de rendimento.
Chris Marisic

Respostas:

575

Você pode usar list ?? Enumerable.Empty<Friend>()ou ter FindFriendsretornoEnumerable.Empty<Friend>()

Michael Mrozek
fonte
7
Mudaria as coisas se ele voltasse, digamos, new List<Friend>()já que será lançado IEnumerable<Friend>quando retornado desse método?
Sarah Vessels
73
new List<Friend>()é uma operação mais caro porque iria criar uma instância de uma lista (e alocar memória para ele no processo)
Igor Pashchuk
106

Quanto a mim, a maneira mais elegante é yield break

Pavel Tupitsyn
fonte
8
Mas isso é se você usar retorno de rendimento e tal, não é?
Svish
15
+1 como seu código corretamente deve usar o rendimento para o jeito que ele está trabalhando com IEnumerable
Chris Marisic
6
Perdoe minha ignorância sobre o assunto, mas você poderia ilustrar como usar a quebra de rendimento nesse contexto? Eu vi exemplos apenas em loops, mas isso não mostra uma imagem clara para mim.
Sergio Tapia
Atualizou a resposta com um exemplo. Realmente é a maneira mais elegante de fazer isso, eu concordo. :)
Johny Skovdal
4
Editar foi rejeitado em revisão por pares, então aqui está o exemplo que eu estava falando @Pyritie - a formatação fica confuso embora, então eu adicionei-lo para pastebin.com/X9Z49Vq1 assim: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

Obviamente, isso é apenas uma questão de preferência pessoal, mas eu escreveria essa função usando yield 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
fonte
1

Eu acho que a maneira mais simples seria

 return new Friend[0];

Os requisitos do retorno são apenas que o método retorne um objeto que implementa IEnumerable<Friend>. O fato de que, em diferentes circunstâncias, você retorna dois tipos diferentes de objetos é irrelevante, desde que ambos implementem IEnumerable.

James Curran
fonte
5
Enumerable.Empty <T> realmente retorna uma matriz vazia de T (T [0]), com a vantagem de que a mesma matriz vazia é reutilizada. Observe que essa abordagem não é ideal para matrizes não vazias, porque os elementos podem ser modificados (no entanto, uma matriz não pode ser redimensionada, o redimensionamento envolve a criação de uma nova instância).
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
fonte