Soy nuevo en programación de C # y tengo el siguiente escenario.

Estoy usando una API que devuelve IEnumerable, que quiero iterar en función de algunas propiedades de objeto:

IEnumerable<objects> listOfObjects = filter.getItems(id);
 List<CustomObject> sortedList = new List<CustomObject>();
            foreach (CustomObject obj in listOfObjects )
            {
                obj.Load(Load.Expanded);
                sortedList.Add(obj);
            }
 foreach (CustomObject custObj in sortedList.OrderByDescending(c => c.RevisionDate))
            {
                // business logic
            }

Necesito hacer todo lo anterior porque no puedo escribir el objeto devuelto en la consulta de filtro. Además, el objeto devuelto por la consulta de filtro no se carga, lo que significa que si no ejecuto el primer bucle foreach, el valor RevisionDate en el segundo foreach será nulo.

Me pregunto si hay una mejor manera de manejar este escenario y ¿se puede eliminar este número de líneas con solo 1 bucle?

1
user3306543 23 oct. 2017 a las 18:03

3 respuestas

La mejor respuesta

Debería poder utilizar el método IEnumerable Select.

Consulte la documentación aquí.

En resumen, querrás hacer algo como esto para eliminar el primer bucle:

List<CustomObject> sortedList = filter.getItems(id).Select<object, CustomObject>(x => 
          {
             (CustomObject)x).Load(Load.Expanded); 
             return (CustomObject)x;  
           });
1
JESteph 23 oct. 2017 a las 16:14

Hay varias construcciones especiales en las bibliotecas de C # que fueron diseñadas para manejar la inicialización diferida. Cada uno puede tener algunas peculiaridades que pueden adaptarse mejor o peor a sus necesidades.

El primero es la clase System.Lazy<T>, que tiene un mejor rendimiento sin la sobrecarga de la clase System.Threading.LazyInitializer con un montón de métodos estáticos y proporciona datos específicos de subproceso System.Threading.ThreadLocal<T>.

El uso de Lazy<T> es simple:

// Initialize by using default Lazy<T> constructor. The 
// Orders array itself is not created yet.
Lazy<Orders> _orders = new Lazy<Orders>();

// Initialize by invoking a specific constructor on Order which
// will be used when Value property is accessed
Lazy<Orders> _orders = new Lazy<Orders>(() => new Orders(100));

// Lazy<Orders> will create the array only if displayOrders is 
// which will go through path where _orders.Value is accessed
if (displayOrders == true)
{
    DisplayOrders(_orders.Value.OrderData);
}
else
{
    // Don't waste resources getting order data.
} 

En lugar de pasar lambda con el constructor, puede pasar lambda al lanzar sus objetos a CustomObject. Si se esfuerza por obtener el mejor rendimiento, evite Linq y hágalo de manera procesal.

Los ejemplos son de: Lazy Initialization Microsoft Docs con comentarios cambiado por mí para mayor claridad y se puede aplicar casi directamente a su código

0
Jacek Blaszczynski 23 oct. 2017 a las 15:47

Puede hacerlo en una declaración linq como esta:

foreach (var custObj in listOfObjects
                       .Cast<CustomObject>()
                       .Select(obj => {obj.Load(); return obj;})
                       .OrderByDescending(c => c.RevisionDate))

Tenga en cuenta que tal uso de Select generalmente se desaconseja y no es una muy buena práctica (tener algo que tenga efectos secundarios como obj.Load en Select).

4
Evk 23 oct. 2017 a las 15:32