Existe uma maneira comum de passar um único item do tipo T
para um método que espera um IEnumerable<T>
parâmetro? O idioma é C #, versão 2.0 do framework.
Atualmente, estou usando um método auxiliar (é .Net 2.0, então eu tenho vários métodos auxiliares de projeção / projeção semelhantes ao LINQ), mas isso parece bobo:
public static class IEnumerableExt
{
// usage: IEnumerableExt.FromSingleItem(someObject);
public static IEnumerable<T> FromSingleItem<T>(T item)
{
yield return item;
}
}
Outra maneira, é claro, seria criar e preencher um List<T>
ou um Array
e passá-lo em vez de IEnumerable<T>
.
[Editar] Como método de extensão, ele pode ser chamado:
public static class IEnumerableExt
{
// usage: someObject.SingleItemAsEnumerable();
public static IEnumerable<T> SingleItemAsEnumerable<T>(this T item)
{
yield return item;
}
}
Estou faltando alguma coisa aqui?
[Edit2] Descobrimos someObject.Yield()
(como @ Peter sugeriu nos comentários abaixo) o melhor nome para esse método de extensão, principalmente por questões de concisão, então aqui está o comentário XML, se alguém quiser pegá-lo:
public static class IEnumerableExt
{
/// <summary>
/// Wraps this object instance into an IEnumerable<T>
/// consisting of a single item.
/// </summary>
/// <typeparam name="T"> Type of the object. </typeparam>
/// <param name="item"> The instance that will be wrapped. </param>
/// <returns> An IEnumerable<T> consisting of a single item. </returns>
public static IEnumerable<T> Yield<T>(this T item)
{
yield return item;
}
}
c#
.net
generics
ienumerable
Groo
fonte
fonte
if (item == null) yield break;
Agora você não pode passar nulo, nem tirar proveito do padrão de objeto nulo (trivial) paraIEnumerable
. (foreach (var x in xs)
lida com um vazioxs
muito bem). Aliás, essa função é a unidade monádica da mônada da listaIEnumerable<T>
e, dada a festa de amor da mônada na Microsoft, estou surpreso que algo assim não esteja no quadro em primeiro lugar.AsEnumerable
porque já existe uma extensão interna com esse nome . (QuandoT
implementaIEnumerable
, por exemplo,string
.)Yield
? Nada supera a brevidade.left==null
cheque aqui. Ele quebra as belezas do código e deixa o código mais flexível - e se algum dia você descobrir que precisa gerar um singleton com algo que pode ser nulo? Quero dizer,new T[] { null }
não é o mesmo quenew T[] {}
, e um dia você pode precisar distingui-los.Respostas:
Seu método auxiliar é a maneira mais limpa de fazer isso, IMO. Se você passar uma lista ou uma matriz, um código inescrupuloso pode convertê-lo e alterar o conteúdo, levando a um comportamento estranho em algumas situações. Você pode usar uma coleção somente leitura, mas é provável que envolva ainda mais quebra automática. Eu acho que sua solução é a mais limpa possível.
fonte
myDict.Keys.AsEnumerable()
ondemyDict
era do tipoDictionary<CheckBox, Tuple<int, int>>
. Depois de renomear o método de extensão paraSingleItemAsEnumerable
, tudo começou a funcionar. Não é possível converter IEnumerable <Dictionary <CheckBox, System.Tuple <int, int >>. KeyCollection> 'para' IEnumerable <CheckBox> '. Existe uma conversão explícita (você está perdendo um elenco?) Posso viver com um hack por um tempo, mas outros hackers avançados podem encontrar uma maneira melhor?dictionary.Values
começar. Basicamente, não está claro o que está acontecendo, mas sugiro que você inicie uma nova pergunta sobre isso.Bem, se o método espera
IEnumerable
que você precise passar algo que é uma lista, mesmo que contenha apenas um elemento.passagem
como o argumento deve ser suficiente, eu acho
fonte
No C # 3.0, você pode utilizar a classe System.Linq.Enumerable:
Isso criará um novo IEnumerable que contém apenas seu item.
fonte
new[]{ item }
, eu apenas me uso , apenas pensei que era uma solução interessante.No C # 3 (eu sei que você disse 2), você pode escrever um método de extensão genérico que pode tornar a sintaxe um pouco mais aceitável:
código do cliente é então
item.ToEnumerable()
.fonte
ToEnumerable
evitar bagunçar comSystem.Linq.Enumerable.AsEnumerable
ToEnumerable
método de extensão paraIObservable<T>
, portanto, esse nome de método também interfere nas convenções existentes. Honestamente, sugiro não usar um método de extensão, simplesmente porque é muito genérico.Este método auxiliar funciona para itens ou muitos.
fonte
params
construção da matriz, para que o código resultante tenha uma aparência limpa. Ainda não decidi se gosto mais ou menos do quenew T[]{ item1, item2, item3 };
para vários itens.Estou surpreso que ninguém tenha sugerido uma nova sobrecarga do método com um argumento do tipo T para simplificar a API do cliente.
Agora, seu código de cliente pode fazer isso:
ou com uma lista:
fonte
DoSomething<T>(params T[] items)
que significa que o compilador lidaria com a conversão de um único item em uma matriz. (Isso também permitiria que você passar em vários itens separados e, mais uma vez, o compilador iria lidar com convertê-los para uma matriz para você.)Qualquer um (como foi dito anteriormente)
ou
Como observação lateral, a última versão também pode ser boa se você quiser uma lista vazia de um objeto anônimo, por exemplo
fonte
Como acabei de encontrar e vi que o usuário LukeH sugeriu também, uma maneira simples e agradável de fazer isso é a seguinte:
Esse padrão permitirá que você chame a mesma funcionalidade de várias maneiras: um único item; vários itens (separados por vírgula); uma matriz; uma lista; uma enumeração etc.
Não tenho 100% de certeza sobre a eficiência do uso do método AsEnumerable, mas funciona muito bem.
Atualização: A função AsEnumerable parece bastante eficiente! ( referência )
fonte
.AsEnumerable()
nada. MatrizYourType[]
já implementadaIEnumerable<YourType>
. Mas minha pergunta estava se referindo ao caso em que apenas o segundo método (no seu exemplo) está disponível e você está usando o .NET 2.0 e deseja passar um único item.IEnumerable<T> MakeEnumerable<T>(params T[] items) {return items;}
método? Pode-se então usar isso com qualquer coisa que se esperaIEnumerable<T>
, para qualquer número de itens distintos. O código deve ser essencialmente tão eficiente quanto definir uma classe especial para retornar um único item.Embora seja um exagero para um método, acredito que algumas pessoas possam achar úteis as extensões interativas.
As extensões interativas (Ix) da Microsoft incluem o seguinte método.
Que pode ser utilizado assim:
Ix adiciona nova funcionalidade não encontrada nos métodos de extensão Linq originais e é um resultado direto da criação das Extensões Reativas (Rx).
Pense,
Linq Extension Methods
+Ix
=Rx
paraIEnumerable
.Você pode encontrar Rx e Ix no CodePlex .
fonte
Isso é 30% mais rápido que
yield
ouEnumerable.Repeat
quando usadoforeach
devido a essa otimização do compilador C # e com o mesmo desempenho em outros casos.neste teste:
fonte
new [] { i }
opção é 3,2 vezes mais rápida que a sua opção. Além disso, sua opção é cerca de 1,2 vezes mais rápida que a extensão comyield return
.SingleEnumerator GetEnumerator()
que retorne sua estrutura. O compilador C # usará esse método (ele pesquisa via digitação de pato) em vez dos da interface e, assim, evita o boxe. Muitas coleções internas utilizam esse truque, como Lista . Nota: isso só funcionará quando você tiver uma referência diretaSingleSequence
, como no seu exemplo. Se você armazená-lo em umaIEnumerable<>
variável, o truque não funcionará (o método da interface será usado)IanG tem um bom post sobre o assunto , sugerindo
EnumerableFrom()
como o nome (e menciona que Haskell e Rx o chamamReturn
).O IIRC F # também chama Retorno. F #Seq
chama o operadorsingleton<'T>
.Tentador, se você está preparado para ser cêntrico em C #, é chamá-lo
Yield
[aludindo aosyield return
envolvidos na realização].Se você está interessado nos aspectos de desempenho, James Michael Hare também tem uma postagem de zero ou um item retornando, o que vale uma varredura.
fonte
Isso pode não ser melhor, mas é bem legal:
fonte
Concordo com os comentários do @ EarthEngine na postagem original, que é 'AsSingleton' é um nome melhor. Veja esta entrada da Wikipedia . Da definição de singleton, segue-se que, se um valor nulo for passado como argumento de que 'AsSingleton' retornará um IEnumerable com um único valor nulo, em vez de um IEnumerable vazio, o que encerraria o
if (item == null) yield break;
debate. Eu acho que a melhor solução é ter dois métodos: 'AsSingleton' e 'AsSingletonOrEmpty'; onde, no caso de um nulo ser passado como argumento, 'AsSingleton' retornará um único valor nulo e 'AsSingletonOrEmpty' retornará um IEnumerable vazio. Como isso:Então, esses seriam, mais ou menos, análogos aos métodos de extensão 'First' e 'FirstOrDefault' no IEnumerable que parecem adequados.
fonte
A maneira mais fácil que eu diria seria
new T[]{item};
; não há sintaxe para fazer isso. O equivalente mais próximo que consigo pensar é aparams
palavra - chave, mas é claro que exige que você tenha acesso à definição do método e é utilizável apenas com matrizes.fonte
O código acima é útil quando se usa como
No trecho de código acima, não há verificação vazia / nula e é garantido que apenas um objeto seja retornado sem medo de exceções. Além disso, por ser preguiçoso, o fechamento não será executado até que seja provado que não há dados existentes que atendam aos critérios.
fonte
MyData.Where(myCondition)
estiver vazia, que já é possível (e mais simples) comDefaultIfEmpty()
:var existingOrNewObject = MyData.Where(myCondition).DefaultIfEmpty(defaultValue).First();
. Isso pode ser simplificado ainda maisvar existingOrNewObject = MyData.FirstOrDefault(myCondition);
se você quiserdefault(T)
e não com um valor personalizado.Estou um pouco atrasado para a festa, mas vou compartilhar o meu caminho de qualquer maneira. Meu problema era que eu queria vincular o ItemSource ou um WPF TreeView a um único objeto. A hierarquia fica assim:
Projeto> Lote (s)> Quarto (s)
Sempre haveria apenas um projeto, mas eu ainda queria mostrar o projeto na árvore, sem ter que passar uma coleção com apenas esse objeto, como sugerido por alguns.
Como você só pode passar objetos IEnumerable como ItemSource, decidi fazer minha classe IEnumerable:
E crie meu próprio enumerador de acordo:
Provavelmente, essa não é a solução "mais limpa", mas funcionou para mim.
EDIT
Para manter o princípio de responsabilidade única, como o @Groo apontou, criei uma nova classe de wrapper:
Que eu usei assim
EDIT 2
Corrigi um erro com o
MoveNext()
método.fonte
SingleItemEnumerator<T>
classe faz sentido, mas fazer da classe um "item únicoIEnumerable
em si" parece uma violação do princípio da responsabilidade única. Talvez isso torne a transmissão mais prática, mas eu ainda preferiria envolvê-lo conforme necessário.Para ser apresentado em "Não necessariamente uma boa solução, mas ainda assim ... uma solução" ou "Truques LINQ estúpidos", você pode combinar
Enumerable.Empty<>()
comEnumerable.Append<>()
...... ou
Enumerable.Prepend<>()
...Os dois últimos métodos estão disponíveis desde o .NET Framework 4.7.1 e o .NET Core 1.0.
Esta é uma solução viável se alguém realmente pretender usar os métodos existentes, em vez de escrever os seus próprios, embora eu esteja indeciso se isso é mais ou menos claro do que a
Enumerable.Repeat<>()
solução . Esse é definitivamente um código mais longo (em parte devido à inferência de parâmetro de tipo não ser possívelEmpty<>()
) e, no entanto, cria o dobro de objetos enumeradores.Completando este "Você sabia que esses métodos existem?" resposta,
Array.Empty<>()
poderia ser substituídoEnumerable.Empty<>()
, mas é difícil argumentar que torna a situação ... melhor.fonte
Recentemente, perguntei a mesma coisa em outro post ( existe uma maneira de chamar um método C # que exija um IEnumerable <T> com um único valor? ... com benchmarking ).
Eu queria que as pessoas que estivessem aqui aparecessem para ver a breve comparação de benchmark mostrada naquele post mais recente para 4 das abordagens apresentadas nessas respostas.
Parece que simplesmente escrever
new[] { x }
os argumentos do método é a solução mais curta e rápida.fonte