Enumeração ordenada: IEnumerable ou Array (em C #)?

8

Contexto típico: Eu faço um método de extensão para uma coleção que considera que os elementos estão ordenados. A função começa no início, no índice 0, e a ordem tem significado. Exemplos: agrupando por sequência ou índices de um item .

No entanto, estou sempre perplexo quanto ao que estender: IEnumerable<T>ou T[]. Minha lógica era a clareza de propósito: uma matriz tem uma noção de ordenação, enquanto um IEnumerable é implementado por muitas coleções genéricas, nem todas com noção de ordem:

  • Dicionário - não ordenado
  • HashSet - não ordenado
  • LinkedList - encomendado
  • Lista - ordenada
  • Fila - solicitada
  • SortedDictionary - classificado (não na ordem original)
  • SortedList - ordenada (não na ordem original)
  • SortedSet - classificado (não na ordem original)
  • Pilha - invertida

Bem como qualquer outra implementação que possa ou não ser solicitada.

Além disso, não tenho certeza se o enumerador é redefinido se uma enumeração não for concluída. Portanto, se isso for uma preocupação, quem sabe em que ponto a enumeração seria iniciada? Enumerar uma matriz sempre começaria no começo.

Então, para mim, faz mais sentido estender a T[]. Mas estou correto ao pensar isso? Estou me preocupando demais? Qual é a abordagem adequada para garantir a enumeração "ordenada"?

MPelletier
fonte

Respostas:

13

Não tenho certeza se o enumerador é redefinido se uma enumeração não for concluída. Portanto, se isso for uma preocupação, quem sabe em que ponto a enumeração seria iniciada? Enumerar uma matriz sempre começaria no começo.

Essas duas frases me fazem pensar que você tem um profundo mal-entendido sobre como o padrão enumerável funciona. Você pode explicar por que você acha que um enumerador abandonado tem algo a ver com uma enumeração posterior ?

Um enumerável é um dispositivo que produz enumeradores . Se um enumerador for abandonado, isso não afeta o enumerável. Se eu vendo livros, e Bob compra um livro e o lê apenas pela metade, isso não significa que quando eu vendo uma cópia diferente do livro para Alice, ela precisa começar a ler de onde parou.

Estou sempre perplexo quanto ao que estender: IEnumerable<T>ou T[]. Minha lógica era a clareza de propósito: uma matriz tem uma noção de ordenação, enquanto um IEnumerable é implementado por muitas coleções genéricas, nem todas com noção de ordem:

Seu método de extensão precisa (1) acessar a coleção fora de ordem? (2) escrever para a coleção?

Se sim, então estenda IList<T>Se não, estenda IEnumerable<T>.

Que tal: (3) passar a coleção para um método que espera uma matriz?

Em seguida, estenda a matriz.

Sua pergunta é basicamente "Não sei se estender Animal ou Girafa". Se o seu método de extensão não for específico para girafas, estenda Animal. Se o seu método de extensão não for específico para matrizes, estenda todas as listas. Se não for específico para listas, estenda todas as seqüências. Se não for específico para seqüências, é provável que um método de extensão seja o mecanismo errado para você; Eu recomendo não estender todos os objetos.

Eric Lippert
fonte
Eu tenho uma grande e vergonhosa falta de conhecimento de como o padrão inumerável funciona. Eu acho que é por isso que estou aqui perguntando isso.
MPelletier
Porra, eu tinha esquecido totalmente minha classe de estrutura de dados. Claro que não afeta enumerações posteriores! Eu cobri-me de vergonha ... :(
MPelletier
@ MPelletier: Sua pergunta ainda tem valor. É seguro / faz sentido executar um algoritmo em que a ordem é importante em, por exemplo, a Dictionary? Provavelmente não. Como Scott Meyers disse: "A melhor maneira de impedir o uso incorreto é tornar esse uso impossível". . É possível / seria benéfico fazê-lo neste cenário?
Steven Jeuris
1
@ EricLippert: Os três pontos que você mencionou são razões pelas quais você precisa estender o array. A verdadeira questão é, como no meu comentário anterior, valeria a pena impedir o uso incorreto? Esperando que você possa expandir um pouco ...
Steven Jeuris
@StevenJeuris Sua pergunta não seria necessária se o C # fosse projetado corretamente e tivesse uma interface ISortedCollection <T>, que você estenderia se precisasse que a coleção fosse classificada. Mas, como isso não ocorre, a única maneira de impedir o uso indevido em tempo de compilação é estender um tipo excessivamente estreito (IList <T>) ou estender IEnumerable <T> e fazer uma classificação desnecessária.
Jim Balter
2

Eu acho que você deseja combinar 2 conceitos diferentes em 1. A estrutura de dados e sua ordem de conteúdo são duas coisas separadas.

Encomendar é um assunto complicado. Por exemplo, o que significa ordenar uma lista de pessoas? Mesmo quando a chave de ordem / classificação é definida, ela precisa ter uma direção e você precisa decidir o que fazer com nulos (se houver), problemas de data, etc.

Se a ordem dos dados for importante para o seu método, o método poderá ser responsável pela ordem dos dados como parte de sua configuração, em vez de solicitar que os dados sejam solicitados pelo chamador. Uma abordagem semelhante é usada por alguns algoritmos de correspondência de string, nos quais um dicionário é criado dentro do método de pesquisa a partir da string passada antes da pesquisa. Sei que esse não é o caso exato, mas é o exemplo mais próximo que pude pensar.

NoChance
fonte
2
Um método de ordenação como argumento ... Hmmm, interessante!
MPelletier
Mesmo assim, a ordem pode estar implícita. Essa é a questão principal que estou tendo. Entendo que o conceito de LINQ é emular funções de banco de dados e, de acordo com o 1NF, os registros não são ordenados. Infelizmente, o mundo real raramente é 1NF.
MPelletier
"o mundo real raramente é 1NF", é verdade em alguns casos, mas não sei como isso afetaria o caso que você descreve aqui?
NoChance
1NF : "Não há pedidos de cima para baixo nas linhas." É o que está acontecendo aqui, eu acho. Há pedidos de cima para baixo em logs, em várias "coleções" frouxas. O LINQ foi projetado para funcionar em coleções como se fossem bancos de dados, mas pode haver pedidos de cima para baixo, daí a contradição que acabei de ver.
MPelletier
1

IEnumerable declara que a coleção que você possui pode ser enumerada; isso não implica nada sobre o conteúdo. Isso não é uma coisa ruim - os contêineres podem ter recursos e restrições adicionais sobre as interfaces individuais.

(Não vejo como uma matriz é melhor aqui - é indexável, como é um IList e outros contêineres, mas não há nada na indexação que implique ordem dos dados. Sua lista é meio estranha - alguns itens são "ordenados "na medida em que preservam a ordem de inserção, outras são" ordenadas ", assim como são classificadas. São duas coisas diferentes. Além do SortedSet / SortedList / SortedDictionary, você não deve assumir que os dados estão em uma ordem específica, a menos que você lide com isso. dentro do seu próprio código.)

No entanto, IEnumerable fornece uma extensão LINQ que permite .OrderBy, para que qualquer coisa enumerável possa ser retornada em uma ordem classificada IOrderedEnumerable. Porém, essa é uma interface específica para LINQ (e não implementada por padrão por SortedSet / SortedList / SortedDictionary, portanto, pode não fazer sentido estender essa interface específica ...)

Joe
fonte
2
O que eu quero evitar é criar uma série de funções que funcionem "apenas se solicitadas"; meses depois, alguém usa a biblioteca em uma coleção não ordenada e se pergunta por que está dando resultados desagradáveis. Além de boa documentação, que todos terão lido completamente, é claro.
MPelletier
E não acho que estender o IOrderedEnumerable fará de mim um fã. Algumas listas já estão ordenadas (pense em um arquivo de log analisado, por exemplo. A ordem é o número da linha, mas não há sentido em classificá- lo se for lido em seqüência.
MPelletier
Atualmente, não existe um decorador / interface que informe / avise os usuários que uma coleção classificada é necessária para o seu método de extensão. Especialmente para dados arbitrários, como você saberia (sem uma função comparadora) se os dados foram ordenados ou não?
Joe