usa - tipos anonimos en c#
¿Cómo puedo convertir un tipo anónimo a un tipo fuerte en LINQ? (3)
Tengo una matriz de ListViewItems ( ListViewItem[]
), donde SalesOrderMaster
un objeto SalesOrderMaster
en cada ListViewItem.Tag para referencia posterior.
Tengo un código que ahora mismo, a través de cada ListViewItem
convierte de forma segura la propiedad .Tag en un objeto SalesOrderMaster, luego agrega ese objeto a una colección de SalesOrders, solo después de verificar que la orden no existe en esa colección.
El proceso para comparar pedidos de ventas es costoso, y me gustaría convertirlo en una expresión LINQ para mayor claridad y rendimiento. (También tengo instaladas las Extensiones paralelas a .NET Framework 3.5, por lo que puedo usar eso para mejorar aún más el rendimiento de LINQ)
Así que sin más preámbulos: esto es lo que tengo, y luego lo que quiero. (Lo que quiero no se compila, así que sé que estoy haciendo algo mal, pero espero que ilustre el punto)
Lo que tengo: (lento)
foreach (ListViewItem item in e.Argument as ListViewItem[])
{
SalesOrderMaster order = item.Tag as SalesOrderMaster;
if ( order == null )
{
return;
}
if (!All_SalesOrders.Contains(order))
{
All_SalesOrders.Add(order);
}
}
Lo que quiero: (teoría)
List<SalesOrderMaster> orders =
(from item in (e.Argument as ListViewItem[]).AsParallel()
select new { ((SalesOrderMaster)item.Tag) }).Distinct();
EDITAR: Sé que el reparto es barato, dije la "Comparación", que en este caso se traduce en la operación .Contains (orden)
EDITAR: La respuesta de todos fue increíble! Me gustaría poder marcar más de una respuesta, pero al final tengo que elegir una.
EDIT: esto es lo que terminé con:
List<SalesOrderMaster> orders =
(from item in (e.Argument as ListViewItem[]) select (SalesOrderMaster) item.Tag).GroupBy(item => item.Number).Select(x => x.First()).ToList();
Como dijo Marc Gravell, no deberías acceder a la propiedad Tag
desde diferentes hilos, y el reparto es bastante barato, por lo que tienes:
var items = (e.Argument as ListViewItem[]).Select(x=>x.Tag)
.OfType<SalesOrderMaster>().ToList();
pero luego, desea buscar elementos distintos: aquí puede intentar usar AsParallel
:
var orders = items.AsParallel().Distinct();
La parte de ese código que es costoso es llamar al método Contains
en la lista. Como es una operación O (n), se vuelve más lento a medida que se agregan más objetos a la lista.
Simplemente use un HashSet<SalesOrderMaster>
para los objetos en lugar de un List<SalesOrderMaster>
. El método Contains
del HashSet
es una operación O (1), por lo que su bucle será una operación O (n) en lugar de una operación O (n * n).
Veo que nadie ha abordado su necesidad de convertir un tipo anónimo a un tipo nombrado explícitamente, así que aquí va ... Al usar " select new { }
" está creando un tipo anónimo, pero no es necesario. Puedes escribir tu consulta de esta manera:
List<SalesOrderMaster> orders =
(from item in (e.Argument as ListViewItem[]).AsParallel()
select (SalesOrderMaster)item.Tag)
.Distinct()
.ToList();
Observe que la consulta selecciona el elemento (SalesOrderMaster)item.Tag
sin new { }
, por lo que no crea un tipo anónimo. También tenga en cuenta que agregué ToList()
ya que desea una List<SalesOrderMaster>
.
Esto resuelve su problema de tipo anónimo. Sin embargo, estoy de acuerdo con Mark y Guffa en que usar una consulta paralela aquí no es su mejor opción. Para usar HashSet<SalesOrderMaster>
como sugirió Guffa, puedes hacer esto:
IEnumerable<SalesOrderMaster> query =
from item in (ListViewItem[])e.Argument
select (SalesOrderMaster)item.Tag;
HashSet<SalesOrderMaster> orders = new HashSet<SalesOrderMaster>(query);
(Evité usar var
para que los tipos devueltos queden claros en los ejemplos).