what read explained c# .net generics ienumerable

read - set ienumerable c#



Pasando un solo elemento como IEnumerable<T> (15)

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.

public static IEnumerable<TResult> Return<TResult>(TResult value) { yield return value; }

Que se puede utilizar como tal:

var result = EnumerableEx.Return(0);

Ix agrega una nueva funcionalidad que no se encuentra en los métodos de extensión Linq originales, y es un resultado directo de la creación de las Extensiones reactivas (Rx).

Piensa, Linq Extension Methods + Ix = Rx para IEnumerable .

Puede encontrar tanto Rx como Ix en CodePlex .

¿Hay una manera común de pasar un solo elemento de tipo T a un método que espera un IEnumerable<T> ? El lenguaje es C #, framework versión 2.0.

Actualmente estoy usando un método de ayuda (es .Net 2.0, así que tengo un montón de métodos de ayuda 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 rellenar una List<T> o un 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 que someObject.Yield() (como @Peter sugirió en los comentarios a continuación) es el mejor nombre para este método de extensión, principalmente por brevedad, por lo que aquí está junto con el comentario XML si alguien quiere agarrarlo:

public static class IEnumerableExt { /// <summary> /// Wraps this object instance into an IEnumerable&lt;T&gt; /// 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&lt;T&gt; consisting of a single item. </returns> public static IEnumerable<T> Yield<T>(this T item) { yield return item; } }


Bueno, si el método espera un IEnumerable , debe pasar algo que es una lista, incluso si contiene un solo elemento.

paso

new T[] { item }

Como el argumento debería ser suficiente pienso


Como acabo de encontrar, y he visto que el usuario LukeH también sugirió, una buena forma de hacerlo es la siguiente:

public static void PerformAction(params YourType[] items) { // Forward call to IEnumerable overload PerformAction(items.AsEnumerable()); } public static void PerformAction(IEnumerable<YourType> items) { foreach (YourType item in items) { // Do stuff } }

Este patrón le permitirá llamar a la misma funcionalidad en una multitud de formas: 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 de usar el método AsEnumerable, pero funciona bien.

Actualización: ¡La función AsEnumerable parece bastante eficiente! ( reference )


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:

static class IEnumerableExtensions { public static IEnumerable<T> ToEnumerable<T>(this T item) { yield return item; } }

el código del cliente es entonces item.ToEnumerable() .


En C # 3.0 puede utilizar la clase System.Linq.Enumerable:

// using System.Linq Enumerable.Repeat(item, 1);

Esto creará un nuevo IEnumerable que solo contiene su artículo.


Este método auxiliar es trabajo para artículo o muchos.

public static IEnumerable<T> ToEnumerable<T>(params T[] items) { return items; }


Esto es 30% más rápido que el yield o Enumerable.Repeat cuando se usa en foreach debido a esta optimización del compilador de C # , y del mismo rendimiento en otros casos.

public struct SingleSequence<T> : IEnumerable<T> { public struct SingleEnumerator : IEnumerator<T> { private readonly SingleSequence<T> _parent; private bool _couldMove; public SingleEnumerator(ref SingleSequence<T> parent) { _parent = parent; _couldMove = true; } public T Current => _parent._value; object IEnumerator.Current => Current; public void Dispose() { } public bool MoveNext() { if (!_couldMove) return false; _couldMove = false; return true; } public void Reset() { _couldMove = true; } } private readonly T _value; public SingleSequence(T value) { _value = value; } public IEnumerator<T> GetEnumerator() { return new SingleEnumerator(ref this); } IEnumerator IEnumerable.GetEnumerator() { return new SingleEnumerator(ref this); } }

en esta prueba:

// Fastest among seqs, but still 30x times slower than direct sum // 49 mops vs 37 mops for yield, or c.30% faster [Test] public void SingleSequenceStructForEach() { var sw = new Stopwatch(); sw.Start(); long sum = 0; for (var i = 0; i < 100000000; i++) { foreach (var single in new SingleSequence<int>(i)) { sum += single; } } sw.Stop(); Console.WriteLine($"Elapsed {sw.ElapsedMilliseconds}"); Console.WriteLine($"Mops {100000.0 / sw.ElapsedMilliseconds * 1.0}"); }


Estoy de acuerdo con los comentarios de @ EarthEngine en la publicación original, que dice que ''AsSingleton'' es un nombre mejor. Vea esta entrada de wikipedia . Luego de la definición de singleton se deduce que si se pasa un valor nulo como argumento de que ''AsSingleton'' debería devolver un IEnumerable con un solo valor nulo en lugar de un IEnumerable vacío que liquidaría el if (item == null) yield break; debate. Creo que la mejor solución es tener dos métodos: ''AsSingleton'' y ''AsSingletonOrEmpty''; donde, en caso de que se pase un nulo como argumento, ''AsSingleton'' devolverá un solo valor nulo y ''AsSingletonOrEmpty'' devolverá un IEnumerable vacío. Me gusta esto:

public static IEnumerable<T> AsSingletonOrEmpty<T>(this T source) { if (source == null) { yield break; } else { yield return source; } } public static IEnumerable<T> AsSingleton<T>(this T source) { yield return source; }

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 sienten bien.


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 la palabra clave params , pero, por supuesto, eso requiere que usted tenga acceso a la definición del método y solo se puede utilizar con matrices.


Llego un poco tarde a la fiesta pero compartiré mi camino de todos modos. Mi problema era que quería unir el ItemSource o un TreeView de WPF a un solo objeto. La jerarquía se ve así:

Proyecto> Parcela (s)> Sala (s)

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 sugeridos.
Ya que solo puedes pasar objetos IEnumerable como ItemSource, decidí hacer que mi clase fuera IEnumerable:

public class ProjectClass : IEnumerable<ProjectClass> { private readonly SingleItemEnumerator<AufmassProjekt> enumerator; ... public IEnumerator<ProjectClass > GetEnumerator() => this.enumerator; IEnumerator IEnumerable.GetEnumerator() => this.GetEnumerator(); }

Y crear mi propio enumerador en consecuencia:

public class SingleItemEnumerator : IEnumerator { private bool hasMovedOnce; public SingleItemEnumerator(object current) { this.Current = current; } public bool MoveNext() { if (this.hasMovedOnce) return false; this.hasMovedOnce = true; return true; } public void Reset() { } public object Current { get; } } public class SingleItemEnumerator<T> : IEnumerator<T> { private bool hasMovedOnce; public SingleItemEnumerator(T current) { this.Current = current; } public void Dispose() => (this.Current as IDisposable).Dispose(); public bool MoveNext() { if (this.hasMovedOnce) return false; this.hasMovedOnce = true; return true; } public void Reset() { } public T Current { get; } object IEnumerator.Current => this.Current; }

Probablemente esta no sea la solución "más limpia" pero funcionó para mí.

EDITAR
Para mantener el principio de responsabilidad única como @Groo señaló, creé una nueva clase de envoltorio:

public class SingleItemWrapper : IEnumerable { private readonly SingleItemEnumerator enumerator; public SingleItemWrapper(object item) { this.enumerator = new SingleItemEnumerator(item); } public object Item => this.enumerator.Current; public IEnumerator GetEnumerator() => this.enumerator; } public class SingleItemWrapper<T> : IEnumerable<T> { private readonly SingleItemEnumerator<T> enumerator; public SingleItemWrapper(T item) { this.enumerator = new SingleItemEnumerator<T>(item); } public T Item => this.enumerator.Current; public IEnumerator<T> GetEnumerator() => this.enumerator; IEnumerator IEnumerable.GetEnumerator() => this.GetEnumerator(); }

Que utilicé asi

TreeView.ItemSource = new SingleItemWrapper(itemToWrap);

Editar 2
Corrigí un error con el método MoveNext() .


Me sorprende que nadie haya sugerido una nueva sobrecarga del método con un argumento de tipo T para simplificar la API del cliente.

public void DoSomething<T>(IEnumerable<T> list) { // Do Something } public void DoSomething<T>(T item) { DoSomething(new T[] { item }); }

Ahora su código de cliente puede hacer esto:

MyItem item = new MyItem(); Obj.DoSomething(item);

o con una lista:

List<MyItem> itemList = new List<MyItem>(); Obj.DoSomething(itemList);


O bien (como se ha dicho anteriormente)

MyMethodThatExpectsAnIEnumerable(new[] { myObject });

o

MyMethodThatExpectsAnIEnumerable(Enumerable.Repeat(myObject, 1));

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

var x = MyMethodThatExpectsAnIEnumerable(Enumerable.Repeat(new { a = 0, b = "x" }, 0));


Puede que esto no sea mejor, pero es genial:

Enumerable.Range(0, 1).Select(i => item);


Su método auxiliar es la forma más limpia de hacerlo, OMI. Si pasa en una lista o una matriz, entonces un trozo de código sin escrúpulos podría lanzarlo y cambiar el contenido, lo que provocaría un comportamiento extraño en algunas situaciones. Podría usar una colección de solo lectura, pero es probable que implique más envoltura. Creo que tu solución es tan clara como se puede.


IanG tiene una buena publicación sobre el tema , sugiriendo EnumerableFrom() como nombre y menciona que la discusión señala que Haskell y Rx lo llaman Return . IIRC F # lo llama Volver también . F # ''s Seq llama al operador singleton<''T> .

La tentación si está preparado para ser C #-céntrico es llamarlo Yield [que alude a la yield return del yield return involucrado en su realización].

Si está interesado en los aspectos de rendimiento, James Michael Hare tiene una publicación de cero o uno de los artículos que vale la pena analizar.