Acabei de ler um artigo interessante chamado Ficando muito fofo com retorno de rendimento em c #
Isso me fez pensar qual é a melhor maneira de detectar se um IEnumerable é uma coleção enumerável real ou se é uma máquina de estado gerada com a palavra-chave yield.
Por exemplo, você pode modificar o DoubleXValue (do artigo) para algo como:
private void DoubleXValue(IEnumerable<Point> points)
{
if(points is List<Point>)
foreach (var point in points)
point.X *= 2;
else throw YouCantDoThatException();
}
Pergunta 1) Existe uma maneira melhor de fazer isso?
Pergunta 2) É algo com que eu deveria me preocupar ao criar uma API?
c#
api-design
ConditionRacer
fonte
fonte
ICollection<T>
seria uma escolha melhor, pois nem todas as coleções sãoList<T>
. Por exemplo, matrizes sãoPoint[]
implementadas,IList<T>
mas não sãoList<T>
.IEnumerable
é imutável no sentido de que você não pode modificar uma coleção (adicionar / remover) enquanto a enumera, no entanto, se os objetos na lista são mutáveis, é perfeitamente razoável modificá-los enquanto enumera a lista.ToList
? msdn.microsoft.com/en-us/library/bb342261.aspx Ele não detecta se foi gerado pelo rendimento, mas o torna irrelevante.Respostas:
Sua pergunta, como eu a entendo, parece basear-se em uma premissa incorreta. Deixe-me ver se consigo reconstruir o raciocínio:
O problema é que a segunda premissa é falsa. Mesmo se você pudesse detectar se um determinado IEnumerable foi ou não o resultado de uma transformação de bloco de iterador (e sim, existem maneiras de fazer isso), isso não ajudaria porque a suposição está errada. Vamos ilustrar o porquê.
Tudo bem, temos quatro métodos. S1 e S2 são sequências geradas automaticamente; S3 e S4 são sequências geradas manualmente. Agora suponha que tenhamos:
O resultado para S1 e S4 será 0; toda vez que você enumera a sequência, você obtém uma nova referência a um M criado. O resultado para S2 e S3 será 100; toda vez que você enumera a sequência, você obtém a mesma referência a M da última vez. Se o código de sequência é gerado automaticamente ou não, é ortogonal à questão de saber se os objetos enumerados têm identidade referencial ou não. Essas duas propriedades - geração automática e identidade referencial - na verdade não têm nada a ver uma com a outra. O artigo ao qual você vinculou os confunde um pouco.
A menos que um provedor de sequência esteja documentado como sempre oferecendo objetos com identidade referencial , não é aconselhável supor que ele o faz.
fonte
Eu acho que o conceito principal é que você deve tratar qualquer
IEnumerable<T>
coleção como imutável : você não deve modificar os objetos, deve criar uma nova coleção com novos objetos:(Ou escreva o mesmo de forma mais sucinta usando o LINQ
Select()
.)Se você realmente deseja modificar os itens da coleção, não use
IEnumerable<T>
, useIList<T>
(ou possivelmenteIReadOnlyList<T>
se você não quiser modificar a coleção propriamente dita, apenas itens nela e você está no .Net 4.5):Dessa forma, se alguém tentar usar seu método
IEnumerable<T>
, ele falhará no momento da compilação.fonte
IEnumerable
vez. A necessidade de fazer isso mais de uma vez pode ser evitada chamandoToList()
e iterando sobre a lista, embora no caso específico mostrado pelo OP prefira a solução da svick de ter o DoubleXValue também retorne um IEnumerable. Claro, em código real eu provavelmente usariaLINQ
para gerar esse tipo deIEnumerable
. No entanto, a escolha de svickyield
faz sentido no contexto da pergunta do OP.Select()
. E sobre várias iterações deIEnumerable
: ReSharper é útil com isso, porque pode avisá-lo quando você faz isso.Pessoalmente, acho que o problema destacado nesse artigo decorre do uso excessivo da
IEnumerable
interface por muitos desenvolvedores de C # atualmente. Parece ter se tornado o tipo padrão quando você está exigindo uma coleção de objetos - mas há muitas informações queIEnumerable
simplesmente não informam ... crucialmente: se foi ou não reificado *.Se você achar que precisa detectar se um
IEnumerable
é realmente umIEnumerable
ou outra coisa, a abstração vazou e você provavelmente está em uma situação em que seu tipo de parâmetro é um pouco solto. Se você precisar de informações extras que porIEnumerable
si só não fornecem, torne esse requisito explícito escolhendo uma das outras interfaces comoICollection
ouIList
.Edite para os que recusam. Volte e leia a pergunta com atenção. O OP está pedindo uma maneira de garantir que as
IEnumerable
instâncias sejam materializadas antes de serem passadas para o método API. Minha resposta aborda essa pergunta. Há uma questão mais ampla sobre a maneira correta de usarIEnumerable
, e certamente as expectativas do código colado pelo OP baseiam-se em um mal-entendido dessa questão mais ampla, mas o OP não perguntou como usá-lo corretamenteIEnumerable
ou como trabalhar com tipos imutáveis . Não é justo me rebaixar por não responder a uma pergunta que não foi feita.* Eu uso o termo
reify
, outros usamstabilize
oumaterialize
. Eu não acho que uma convenção comum tenha sido adotada pela comunidade ainda.fonte
ICollection
eIList
é que eles são mutáveis (ou pelo menos parecem assim).IReadOnlyCollection
eIReadOnlyList
do .Net 4.5 resolvem alguns desses problemas.