Cuando crea un método de extensión, puede, por supuesto, llamarlo. null
Pero, a diferencia de una llamada al método de instancia, llamarlo como nulo no tiene que arrojar un NullReferenceException
-> debe verificarlo y hacerlo manualmente.
Para la implementación del método de extensión Linq, Any()
Microsoft decidió que deberían lanzar un ArgumentNullException
( https://github.com/dotnet/corefx/blob/master/src/System.Linq/src/System/Linq/AnyAll.cs ).
Me molesta tener que escribir if( myCollection != null && myCollection.Any() )
¿Estoy equivocado, como cliente de este código, al esperar que, por ejemplo ((int[])null).Any()
, vuelva false
?
c#
.net
linq
extension-method
esto se extiende
fuente
fuente
null |> Seq.isEmpty
arrojaSystem.ArgumentNullException: Value cannot be null
. La expectativa parece ser que no pasará valores indefinidos para algo que se espera que exista, por lo que sigue siendo una excepción cuando tiene un valor nulo. Si es una cuestión de inicialización, comenzaría con una secuencia vacía en lugar de anull
.null
cuando se trata de colecciones, sino que debe usar una colección vacía en esos casos.Any
Es simplemente consistente.Respuestas:
Tengo una bolsa con cinco papas. ¿Hay
.Any()
papas en la bolsa?" Sí ", dices.
<= true
Saco todas las papas y las como. ¿Hay
.Any()
papas en la bolsa?" No ", dices.
<= false
Incinero completamente la bolsa en un incendio. ¿Hay
.Any()
papas en la bolsa ahora?" No hay bolsa ".
<= ArgumentNullException
fuente
En primer lugar, parece que ese código fuente arrojará
ArgumentNullException
, noNullReferenceException
.Una vez dicho esto, en muchos casos ya sabe que su colección no es nula, porque este código solo se llama desde el código que sabe que la colección ya existe, por lo que no tendrá que colocar la verificación nula allí con mucha frecuencia. Pero si no sabe que existe, entonces tiene sentido verificar antes de llamarlo
Any()
.Sí. La pregunta que
Any()
responde es "¿esta colección contiene algún elemento?" Si esta colección no existe, entonces la pregunta en sí misma no tiene sentido; no puede contener ni no contener nada, porque no existe.fuente
null
es equivalente a un conjunto que contiene el conjunto vacío (y viceversa)?{ null }
y{ {} }
debería ser equivalente. Me parece fascinante; No puedo relacionarme con eso en absoluto..Add
unanull
colección de alguna manera para nosotros también?Nulo significa información faltante, no elementos.
Podría considerar más ampliamente evitar nulos; por ejemplo, use uno de los enumerables vacíos incorporados para representar una colección sin elementos en lugar de nulo.
Si devuelve nulo en algunas circunstancias, puede cambiar eso para devolver la colección vacía. (De lo contrario, si encuentra nulos devueltos por métodos de biblioteca (no los suyos), eso es desafortunado, y los envolvería para normalizar).
Ver también https://stackoverflow.com/questions/1191919/what-does-linq-return-when-the-results-are-empty
fuente
null
no significa que ningún elemento es cuestión de utilizar una convención sensata. Basta con pensar en OLESTRINGS nativas, donde realmente hace decir eso.null
no significa "información faltante".null
no tiene una sola interpretación significativa. En cambio, es una cuestión de cómo lo tratas.null
a veces se usa para "información faltante", pero también para "sin elementos", y también para "se produjo un error" o "información no inicializada" o "información conflictiva" o alguna cosa arbitraria y ad-hoc.null
se usa con mucha frecuencia para significar "sin datos". A veces tiene sentido. Otras veces no.Aparte de la sintaxis condicional nula , hay otra técnica para aliviar este problema: no permita que su variable permanezca
null
.Considere una función que acepta una colección como parámetro. Si a los efectos de la función,
null
y vacío son equivalentes, puede asegurarse de que nunca contenganull
al principio:Puede hacer lo mismo cuando busca colecciones de algún otro método:
(Tenga en cuenta que en los casos en los que tiene control sobre una función como
GetWords
ynull
es equivalente a la colección vacía, es preferible devolver la colección vacía en primer lugar).Ahora puede realizar cualquier operación que desee en la colección. Esto es especialmente útil si necesita realizar muchas operaciones que fallarían cuando la colección es
null
, y en los casos en que obtenga el mismo resultado al recorrer o consultar un enumerable vacío, permitirá eliminar lasif
condiciones por completo.fuente
Sí, simplemente porque estás en C # y ese comportamiento está bien definido y documentado.
Si estuviera haciendo su propia biblioteca, o si estuviera usando un idioma diferente con una cultura de excepción diferente, entonces sería más razonable esperar falso.
Personalmente, siento que return false es un enfoque más seguro que hace que su programa sea más robusto, pero al menos es discutible.
fuente
Si las comprobaciones nulas repetidas le molestan, puede crear su propio método de extensión 'IsNullOrEmpty ()', para reflejar el método String con ese nombre, y ajustar la llamada null-check y .Any () en una sola llamada.
De lo contrario, la solución, mencionada por @ 17 de 26 en un comentario bajo su pregunta, también es más corta que el método 'estándar' y razonablemente clara para cualquiera que esté familiarizado con la nueva sintaxis condicional nula.
fuente
?? false
lugar de== true
. Esto me parecería más explícito (más limpio) ya que maneja solo el caso denull
, y en realidad no es más detallado. Además, los==
controles de booleanos generalmente desencadenan mi reflejo nauseoso. =)myCollection?.Any()
suficiente?myCollection?.Any()
devuelve efectivamente un booleano anulable en lugar de un booleano ordinario (es decir, bool? En lugar de bool). No hay conversión implícita de bool? bool, y es un bool que necesitamos en este ejemplo en particular (para probar como parte de laif
condición). Por lo tanto, debemos comparar explícitamentetrue
o hacer uso del operador de fusión nula (??).?.
.==
realmente funcionará. Ya sé lo que sucede con un booleano intuitivamente cuando solo puede sertrue
ofalse
; Ni siquiera tengo que pensarlo. Pero tírelonull
a la mezcla, y ahora tengo que averiguar si eso==
es correcto.??
en su lugar, simplemente elimina elnull
caso, reduciéndolo de nuevo atrue
yfalse
, que ya comprende de inmediato. En cuanto a mi reflejo nauseoso, cuando lo veo por primera vez, mi instinto inmediato es simplemente eliminarlo, ya que generalmente es inútil, lo que no quieres que suceda.Si te preguntas sobre las expectativas, debes pensar en las intenciones.
null
significa algo muy diferente deEnumerable.Empty<T>
Como se menciona en la respuesta de Erik Eidt , hay una diferencia de significado entre
null
y una colección vacía.Echemos un primer vistazo a cómo se supone que deben usarse.
El libro Framework Design Guidelines: Convenciones, modismos y patrones para bibliotecas .NET reutilizables, segunda edición, escrito por los arquitectos de Microsoft Krzysztof Cwalina y Brad Abrams establece la siguiente mejor práctica:
Considere llamar a un método que finalmente obtiene datos de una base de datos: si recibe una matriz vacía o
Enumerable.Empty<T>
esto simplemente significa que su espacio de muestra está vacío, es decir, su resultado es un conjunto vacío .null
Sin embargo, recibir en este contexto significaría un estado de error .En la misma línea de pensamiento que la analogía de la papa de Dan Wilson , tiene sentido hacer preguntas sobre sus datos, incluso si se trata de un conjunto vacío . Pero tiene mucho menos sentido, si no hay un conjunto .
fuente
null
y vacío puede resultar en el mismo valor de retorno para la función, pero eso no significa quenull
y vacío significa exactamente lo mismo en el alcance de la llamada.Hay muchas respuestas que explican por qué
null
y las vacías son diferentes y suficientes opiniones que intentan explicar por qué deben tratarse de manera diferente o no. Sin embargo, estás preguntando:Es una expectativa perfectamente razonable . Tienes tanta razón como alguien más que defiende el comportamiento actual. Estoy de acuerdo con la filosofía de implementación actual, pero los factores impulsores no se basan únicamente en consideraciones fuera de contexto.
Dado que
Any()
sin predicado es esencialmenteCount() > 0
, ¿qué esperas de este fragmento?O un genérico:
Supongo que esperas
NullReferenceException
.Any()
es un método de extensión, debe integrarse sin problemas con el objeto extendido y luego lanzar una excepción es lo menos sorprendente.Enumerable.Any(null)
y allí definitivamente lo esperasArgumentNullException
. Es el mismo método y tiene que ser coherente con, posiblemente, casi TODO lo demás en el Marco.null
objeto es un error de programación , el marco no debe aplicar nulo como valor mágico . Si lo usa de esa manera, es su responsabilidad tratarlo.Count()
parece una decisión fácil, entoncesWhere()
no lo es. ¿Qué hay deMax()
? Lanza una excepción para una lista VACÍA, ¿no debería arrojar también para unanull
?Lo que los diseñadores de la biblioteca hicieron antes de LINQ fue introducir métodos explícitos cuando
null
es un valor válido (por ejemploString.IsNullOrEmpty()
) y luego TENÍAN que ser consistentes con la filosofía de diseño existente . Dicho esto, incluso si es bastante trivial de escribir, dos métodosEmptyIfNull()
yEmptyOrNull()
podrían ser útiles.fuente
Se supone que Jim debe dejar papas en cada bolsa. De lo contrario, lo voy a matar.
Jim tiene una bolsa con cinco papas. ¿Hay
.Any()
papas en la bolsa?"Sí", dices. <= verdadero
Ok, entonces Jim vive esta vez.
Jim saca todas las papas y se las come. ¿Hay alguna () papa en la bolsa?
"No", dices. <= falso
Hora de matar a Jim.
Jim incinera completamente la bolsa en un incendio. ¿Hay alguna () papa en la bolsa ahora?
"No hay bolsa". <= ArgumentNullException
¿Debería Jim vivir o morir? Bueno, no esperábamos esto, así que necesito una decisión. ¿Dejar que Jim se salga con la suya es un error o no?
Puede usar anotaciones para indicar que no está soportando ninguna travesura nula de esta manera.
Pero su cadena de herramientas tiene que soportarlo. Lo que significa que probablemente terminarás escribiendo cheques.
fuente
Si esto te molesta tanto, te sugiero un método de extensión simple.
Ahora puedes hacer esto:
... y la bandera se establecerá en
false
.fuente
return
declaración como:return source ?? Enumerable.Empty<T>();
Esta es una pregunta sobre los métodos de extensión de C # y su filosofía de diseño, por lo que creo que la mejor manera de responder a esta pregunta es citar la documentación de MSDN sobre el propósito de los métodos de extensión :
Para resumir, los métodos de extensión están diseñados para agregar métodos de instancia a un tipo particular, incluso cuando los desarrolladores no pueden hacerlo directamente. Y debido a que los métodos de instancia siempre anularán los métodos de extensión si están presentes (si se llama utilizando la sintaxis del método de instancia), esto solo debe hacerse si no puede agregar directamente un método o extender la clase. *
En otras palabras, un método de extensión debería actuar exactamente como lo haría un método de instancia, porque puede terminar siendo un método de instancia por algún cliente. Y debido a que un método de instancia debería arrojar si el objeto al que se está llamando es
null
, también debería hacerlo el método de extensión.* Como nota al margen, esta es exactamente la situación que enfrentaron los diseñadores de LINQ: cuando se lanzó C # 3.0, ya había millones de clientes que usaban
System.Collections.IEnumerable
ySystem.Collections.Generic.IEnumerable<T>
, tanto en sus colecciones como enforeach
bucles. Estas clases volvieronIEnumerator
objetos que sólo tenían los dos métodosCurrent
yMoveNext
, por lo que añadir cualquiera de los métodos instancia necesarios adicionales, tales comoCount
,Any
, etc., sería romper estos millones de clientes. Entonces, para proporcionar esta funcionalidad (especialmente porque se puede implementar en términos deCurrent
yMoveNext
con relativa facilidad), la lanzaron como métodos de extensión, que se pueden aplicar a cualquier sistema existente actualmente.IEnumerable
instancia y también puede ser implementado por clases de maneras más eficientes. Si los diseñadores de C # hubieran decidido lanzar LINQ el primer día, se habrían proporcionado como métodos de instanciaIEnumerable
y probablemente habrían diseñado algún tipo de sistema para proporcionar implementaciones de interfaz predeterminadas de esos métodos.fuente
Creo que el punto principal aquí es que al devolver falso en lugar de lanzar una excepción, está ofuscando información que puede ser relevante para futuros lectores / modificadores de su código.
Si es posible que la lista sea nula, sugeriría tener una ruta lógica separada para eso, ya que de lo contrario en el futuro alguien puede agregar alguna lógica basada en la lista (como un complemento) al else {} de su if, lo que resulta en un excepción no deseada que no tenían razón para predecir.
La legibilidad y la mantenibilidad triunfan 'Tengo que escribir una condición adicional' cada vez.
fuente
Como regla general, escribo la mayor parte de mi código para suponer que la persona que llama es responsable de no entregarme datos nulos, principalmente por los motivos descritos en Di no a nulo (y otras publicaciones similares). El concepto de nulo a menudo no se comprende bien y, por esa razón, es aconsejable inicializar sus variables con anticipación, tanto como sea práctico. Suponiendo que está trabajando con una API razonable, idealmente nunca debería recuperar un valor nulo, por lo que nunca debería tener que verificar si es nulo. El punto nulo, como han dicho otras respuestas, es asegurarse de que tiene un objeto válido (por ejemplo, una "bolsa") para trabajar antes de continuar. Esto no es una característica de
Any
, sino una característica del lenguajesí mismo. No puede realizar la mayoría de las operaciones en un objeto nulo, porque no existe. Es como si te pido que conduzcas a la tienda en mi automóvil, excepto que no tengo un automóvil, por lo que no tienes forma de usar mi automóvil para conducir a la tienda. No tiene sentido realizar una operación en algo que literalmente no existe.Hay casos prácticos para usar nulo, como cuando literalmente no tiene la información solicitada disponible (por ejemplo, si le pregunto cuál es mi edad, la opción más probable para que responda en este momento es "No sé ", que es lo que es un valor nulo). Sin embargo, en la mayoría de los casos, al menos debe saber si sus variables se inicializan o no. Y si no lo hace, le recomendaría que necesita ajustar su código. Lo primero que hago en cualquier programa o función es inicializar un valor antes de usarlo. Es raro que necesite verificar valores nulos, porque puedo garantizar que mis variables no son nulas. Es un buen hábito, y cuanto más se acuerde de inicializar sus variables, con menos frecuencia deberá comprobar si hay valores nulos.
fuente