c# - sintaxis - Extendiendo LINQ para aceptar enumerables anulables
sintaxis linq c# (4)
¿Qué penalizaciones están asociadas con mi método de extensión en tiempo de ejecución?
Su método de extensión se transforma en una máquina de estado, por lo que hay una sobrecarga mínima de eso, pero eso no debería ser perceptible.
¿Es una buena práctica extender linq de esa manera?
En su pregunta usted declara:
Mientras se trabaja con las extensiones de Linq, es normal ver un código como este ( inserte una comprobación nula enumerable aquí )
Y me permito disentir. La práctica común dice que no se devuelva nulo donde se IEnumerable<T>
un IEnumerable<T>
. La mayoría de los casos deben devolver una colección vacía (o IEnumerable
), dejando null
a lo excepcional , porque nulo no está vacío . Esto haría que tu método fuera completamente redundante. Use Enumerable.Empty<T>
cuando sea necesario.
Mientras se trabaja con las extensiones Linq, es normal ver un código como este:
IEnumerable<int> enumerable = GetEnumerable();
int sum = 0;
if (enumerable != null)
{
sum = enumerable.Sum();
}
Con el fin de mejorar la calidad del código, escribí el siguiente método de extensión que verifica los enumerables que admiten nulos y rompe la ejecución de linq.
public static IEnumerable<T> IgnoreIfEmpty<T>(this IEnumerable<T> enumerable)
{
if (enumerable == null) yield break;
foreach (var item in enumerable)
{
yield return item;
}
}
Entonces, puedo refactorizar el código para que sea así:
var sum = GetEnumerable().IgnoreIfEmpty().Sum();
Mis preguntas ahora:
- ¿Qué penalizaciones están asociadas con mi método de extensión en tiempo de ejecución?
- ¿Es una buena práctica extender linq de esa manera?
Actualización: Mi marco de destino es: 3.5
- Tendrá una sobrecarga de llamadas de método, será despreciable a menos que lo esté ejecutando en un bucle cerrado o en un escenario crítico de rendimiento. Es solo una sombra en comparación con algo como una llamada de base de datos o escribir en un sistema de archivos. Tenga en cuenta que es probable que el método no esté en línea, ya que es un enumerador.
- Se trata de legibilidad / mantenibilidad. ¿Qué espero que suceda cuando vea
GetEnumerable().IgnoreIfEmpty().Sum();
? En este caso, tiene sentido.
Tenga en cuenta que con C # 6 podemos usar la siguiente sintaxis: GetEnumerable()?.Sum()
que devuelve un int?
. Podrías escribir GetEnumerable()?.Sum() ?? 0
GetEnumerable()?.Sum() ?? 0
o GetEnumerable()?.Sum().GetValueOrDefault()
para obtener un entero que no sea nulo y que por defecto sea cero.
Si está realmente preocupado por el rendimiento, también podría refactorizar ligeramente su método para que no sea un enumerador. Esto puede aumentar la posibilidad de alinearse, aunque no tengo idea de la lógica ''arcana'' del compilador JIT:
public static IEnumerable<T> IgnoreIfEmpty<T>(this IEnumerable<T> enumerable)
{
if (enumerable == null) return Enumerable.Empty<T>();
return enumerable;
}
En general, sobre la extensión de Linq, creo que está perfectamente bien siempre que el código tenga sentido. MSDN incluso tiene un artículo al respecto. Si observa los métodos estándar de Where
, Select
en Linq y se olvida de las optimizaciones de rendimiento que tienen allí, los métodos son en su mayoría métodos de una sola línea.
La mayoría de las veces escribimos mucho código simplemente porque estamos encantados con la belleza de nuestra creación, no porque realmente lo necesitemos , y luego lo llamamos abstracción, reutilización, extensibilidad, etc.
¿Es esta pieza cruda menos legible o menos extensible o menos reutilizable o más lenta?
var sum = GetEnumerable().Where(a => a != null).Sum();
Cuanto menos código escriba, menos código probará, sea sencillo. Por cierto, es bueno escribir métodos de extensión si puede justificarlo.
Puede omitir el método de extensión adicional y usar el operador de unión nula . Para eso es, y una comprobación única de nulabilidad debería ser mucho más eficiente que otra máquina de estados:
IEnumerable<int> enumerable = GetEnumerable();
int sum = 0;
sum = (enumerable ?? Enumerable.Empty<int>()).Sum();