¿Hay una forma común de pasar un solo elemento de tipo T
a un método que espera un IEnumerable<T>
parámetro? El lenguaje es C #, framework versión 2.0.
Actualmente estoy usando un método auxiliar (es .Net 2.0, así que tengo un montón de métodos auxiliares de proyección / proyección similares a LINQ), pero esto parece una tontería:
public static class IEnumerableExt
{
// usage: IEnumerableExt.FromSingleItem(someObject);
public static IEnumerable<T> FromSingleItem<T>(T item)
{
yield return item;
}
}
Otra forma sería, por supuesto, crear y completar una List<T>
o una Array
y pasarla en lugar de IEnumerable<T>
.
[Editar] Como método de extensión podría llamarse:
public static class IEnumerableExt
{
// usage: someObject.SingleItemAsEnumerable();
public static IEnumerable<T> SingleItemAsEnumerable<T>(this T item)
{
yield return item;
}
}
¿Me estoy perdiendo de algo?
[Edit2] Encontramos someObject.Yield()
(como @Peter sugirió en los comentarios a continuación) el mejor nombre para este método de extensión, principalmente por brevedad, así que aquí está junto con el comentario XML si alguien quiere obtenerlo:
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;
}
}
fuente
if (item == null) yield break;
ahora se le impide pasar nulo y aprovechar el patrón de objeto nulo (trivial)IEnumerable
. (foreach (var x in xs)
maneja un vacíoxs
muy bien). Por cierto, esta función es la unidad monádica de la mónada de la listaIEnumerable<T>
, y dado el festival de amor de la mónada en Microsoft, me sorprende que algo así no esté en el marco en primer lugar.AsEnumerable
porque ya existe una extensión incorporada con ese nombre . (Cuando seT
implementaIEnumerable
, por ejemplo,.string
)Yield
? Nada supera la brevedad.left==null
cheque aquí. Rompe la belleza del código y evita que el código sea más flexible: ¿qué pasa si algún día resulta que necesitas generar un singleton con algo que puede ser nulo? Quiero decir,new T[] { null }
no es lo mismo quenew T[] {}
, y algún día puede que necesites distinguirlos.Respuestas:
Su método de ayuda es la forma más limpia de hacerlo, en mi opinión. Si pasa una lista o una matriz, un código inescrupuloso podría emitirlo y cambiar el contenido, lo que llevaría a un comportamiento extraño en algunas situaciones. Podría usar una colección de solo lectura, pero es probable que implique aún más envoltura. Creo que su solución es tan clara como parece.
fuente
myDict.Keys.AsEnumerable()
dondemyDict
era de tipoDictionary<CheckBox, Tuple<int, int>>
. Después de cambiar el nombre del método de extensión aSingleItemAsEnumerable
, todo comenzó a funcionar. No se puede convertir IEnumerable <Dictionary <CheckBox, System.Tuple <int, int >>. KeyCollection> 'a' IEnumerable <CheckBox> '. Existe una conversión explícita (¿te estás perdiendo un elenco?) Puedo vivir con un hack por un tiempo, pero ¿pueden otros hackers de poder encontrar una mejor manera?dictionary.Values
comenzar. Básicamente no está claro qué está pasando, pero le sugiero que comience una nueva pregunta al respecto.Bueno, si el método espera un
IEnumerable
tienes que pasar algo que es una lista, incluso si contiene un solo elemento.paso
como el argumento debería ser suficiente creo
fuente
En C # 3.0 puede utilizar la clase System.Linq.Enumerable:
Esto creará un nuevo IEnumerable que solo contiene su artículo.
fuente
new[]{ item }
me utilizo , pensé que era una solución interesante.En C # 3 (sé que dijiste 2), puedes escribir un método de extensión genérico que podría hacer que la sintaxis sea un poco más aceptable:
El código del cliente es entonces
item.ToEnumerable()
.fuente
ToEnumerable
evitar meterse con élSystem.Linq.Enumerable.AsEnumerable
ToEnumerable
método de extensión paraIObservable<T>
, por lo que este nombre de método también interfiere con las convenciones existentes. Honestamente, sugeriría no usar un método de extensión, simplemente porque es demasiado genérico.Este método de ayuda funciona para artículos o muchos.
fuente
params
de construir la matriz, por lo que el código resultante tiene un aspecto limpio. No he decidido si me gusta más o menos quenew T[]{ item1, item2, item3 };
para varios artículos.Me sorprende que nadie sugiera una nueva sobrecarga del método con un argumento de tipo T para simplificar la API del cliente.
Ahora su código de cliente puede hacer esto:
o con una lista:
fuente
DoSomething<T>(params T[] items)
que significa que el compilador manejaría la conversión de un solo elemento a una matriz. (Esto también permitirá pasar en varios elementos separados y, de nuevo, el compilador se ocuparía de convertirlos en una matriz para usted.)Cualquiera de los dos (como se ha dicho anteriormente)
o
Como nota al margen, la última versión también puede ser agradable si desea una lista vacía de un objeto anónimo, por ejemplo
fuente
Como acabo de encontrar, y he visto que el usuario LukeH también sugirió, una manera simple y agradable de hacer esto es la siguiente:
Este patrón le permitirá llamar a la misma funcionalidad de muchas maneras: un solo elemento; elementos múltiples (separados por comas); una matriz; una lista; una enumeración, etc.
Sin embargo, no estoy 100% seguro de la eficacia del uso del método AsEnumerable, pero funciona de maravilla.
Actualización: ¡La función AsEnumerable parece bastante eficiente! ( referencia )
fuente
.AsEnumerable()
nada. La matrizYourType[]
ya se implementaIEnumerable<YourType>
. Pero mi pregunta se refería al caso en el que solo está disponible el segundo método (en su ejemplo), y está utilizando .NET 2.0, y desea pasar un solo elemento.IEnumerable<T> MakeEnumerable<T>(params T[] items) {return items;}
método? Entonces se podría usar eso con cualquier cosa que se esperaraIEnumerable<T>
, para cualquier número de elementos discretos. El código debería ser esencialmente tan eficiente como definir una clase especial para devolver un solo elemento.Aunque es excesivo para un método, creo que algunas personas pueden encontrar útiles las extensiones interactivas.
Las Extensiones interactivas (Ix) de Microsoft incluyen el siguiente método.
Que se puede utilizar así:
Ix agrega una nueva funcionalidad que no se encuentra en los métodos de extensión originales de Linq, y es el resultado directo de crear las Extensiones Reactivas (Rx).
Piensa,
Linq Extension Methods
+Ix
=Rx
paraIEnumerable
.Puede encontrar Rx e Ix en CodePlex .
fuente
Esto es 30% más rápido que
yield
oEnumerable.Repeat
cuando se usaforeach
debido a esta optimización del compilador de C # , y del mismo rendimiento en otros casos.en esta prueba:
fuente
new [] { i }
opción es aproximadamente 3.2 veces más rápida que su opción. Además, su opción es aproximadamente 1.2 veces más rápida que la extensión conyield return
.SingleEnumerator GetEnumerator()
que devuelva su estructura. El compilador de C # usará este método (lo busca a través de la escritura de pato) en lugar de los de la interfaz y, por lo tanto, evita el boxeo. Muchas colecciones integradas utilizan este truco, como List . Nota: esto solo funcionará cuando tenga una referencia directa aSingleSequence
, como en su ejemplo. Si lo almacena en unaIEnumerable<>
variable, entonces el truco no funcionará (se utilizará el método de interfaz)IanG tiene una buena publicación sobre el tema , sugiriendo
EnumerableFrom()
su nombre (y menciona que Haskell y Rx lo llamanReturn
).IIRC F # lo llama Devolver también. F # 'sSeq
llama al operadorsingleton<'T>
.Tentador si estás preparado para ser centrado en C # es llamarlo
Yield
[aludiendo alyield return
involucrado en darse cuenta].Si está interesado en los aspectos de rendimiento, James Michael Hare también tiene una publicación de cero o uno de los artículos que vale la pena escanear.
fuente
Puede que esto no sea mejor, pero es genial:
fuente
Estoy de acuerdo con los comentarios de @ EarthEngine a la publicación original, que es que 'AsSingleton' es un nombre mejor. Ver esta entrada de wikipedia . Luego, de la definición de singleton se deduce que si se pasa un valor nulo como argumento, 'AsSingleton' debería devolver un IEnumerable con un solo valor nulo en lugar de un IEnumerable vacío que resolvería el
if (item == null) yield break;
debate. Creo que la mejor solución es tener dos métodos: 'AsSingleton' y 'AsSingletonOrEmpty'; donde, en el caso de que se pase un nulo como argumento, 'AsSingleton' devolverá un único valor nulo y 'AsSingletonOrEmpty' devolverá un IEnumerable vacío. Me gusta esto:Entonces, estos serían, más o menos, análogos a los métodos de extensión 'First' y 'FirstOrDefault' en IEnumerable que simplemente se siente bien.
fuente
La forma más fácil que diría sería
new T[]{item};
; No hay sintaxis para hacer esto. El equivalente más cercano que se me ocurre es laparams
palabra clave, pero, por supuesto, eso requiere que tenga acceso a la definición del método y solo se puede usar con matrices.fuente
El código anterior es útil cuando se usa like
En el fragmento de código anterior no hay verificación vacía / nula, y se garantiza que solo se devolverá un objeto sin temor a excepciones. Además, debido a que es vago, el cierre no se ejecutará hasta que se demuestre que no hay datos existentes que cumplan los criterios.
fuente
MyData.Where(myCondition)
está vacío, que ya es posible (y más simple) conDefaultIfEmpty()
:var existingOrNewObject = MyData.Where(myCondition).DefaultIfEmpty(defaultValue).First();
. Eso se puede simplificar aún másvar existingOrNewObject = MyData.FirstOrDefault(myCondition);
si lo deseadefault(T)
y no un valor personalizado.Llego un poco tarde a la fiesta pero compartiré mi camino de todos modos. Mi problema era que quería vincular el ItemSource o un WPF TreeView a un solo objeto. La jerarquía se ve así:
Proyecto> Parcela (s)> Habitación (es)
Siempre iba a haber un solo Proyecto, pero todavía quería Mostrar el proyecto en el Árbol, sin tener que pasar una Colección con solo ese objeto como algunos sugirieron.
Como solo puede pasar objetos IEnumerable como ItemSource, decidí hacer que mi clase IEnumerable:
Y crear mi propio enumerador en consecuencia:
Probablemente esta no sea la solución "más limpia", pero funcionó para mí.
EDITAR
Para mantener el principio de responsabilidad única como señaló @Groo, creé una nueva clase de contenedor:
Que usé así
EDIT 2
Corrigí un error con el
MoveNext()
método.fuente
SingleItemEnumerator<T>
clase tiene sentido, pero hacer de una clase un "elemento únicoIEnumerable
en sí mismo" parece una violación del principio de responsabilidad única. Tal vez hace que pasarlo sea más práctico, pero aún así preferiría envolverlo según sea necesario.Para ser archivado bajo "No necesariamente una buena solución, pero aún así ... una solución" o "Trucos estúpidos de LINQ", podría combinar
Enumerable.Empty<>()
conEnumerable.Append<>()
...... o
Enumerable.Prepend<>()
...Los dos últimos métodos están disponibles desde .NET Framework 4.7.1 y .NET Core 1.0.
Esta es una solución viable si uno realmente tuviera la intención de usar los métodos existentes en lugar de escribir los suyos, aunque no estoy seguro si esto es más o menos claro que la
Enumerable.Repeat<>()
solución . Este es definitivamente un código más largo (en parte debido a que no es posible la inferencia de parámetros de tipoEmpty<>()
) y, sin embargo, crea el doble de objetos enumeradores.Completando este "¿Sabía que existen estos métodos?" respuesta,
Array.Empty<>()
podría ser sustituidoEnumerable.Empty<>()
, pero es difícil argumentar que hace que la situación ... sea mejor.fuente
Recientemente pregunté lo mismo en otra publicación ( ¿Hay alguna manera de llamar a un método C # que requiera un IEnumerable <T> con un solo valor? ... con benchmarking ).
Quería que la gente se detuviera aquí para ver la breve comparación de referencia que se muestra en esa publicación más reciente para 4 de los enfoques presentados en estas respuestas.
Parece que simplemente escribir
new[] { x }
los argumentos del método es la solución más corta y rápida.fuente