Cuando crea un método de extensión, puede, por supuesto, llamarlo. nullPero, 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.isEmptyarrojaSystem.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.nullcuando se trata de colecciones, sino que debe usar una colección vacía en esos casos.AnyEs simplemente consistente.Respuestas:
Tengo una bolsa con cinco papas. ¿Hay
.Any()papas en la bolsa?" Sí ", dices.
<= trueSaco todas las papas y las como. ¿Hay
.Any()papas en la bolsa?" No ", dices.
<= falseIncinero completamente la bolsa en un incendio. ¿Hay
.Any()papas en la bolsa ahora?" No hay bolsa ".
<= ArgumentNullExceptionfuente
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
nulles 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..Addunanullcolecció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
nullno 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.nullno significa "información faltante".nullno tiene una sola interpretación significativa. En cambio, es una cuestión de cómo lo tratas.nulla 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.nullse 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,
nully vacío son equivalentes, puede asegurarse de que nunca contenganullal 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
GetWordsynulles 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 lasifcondiciones 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
?? falselugar 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 laifcondición). Por lo tanto, debemos comparar explícitamentetrueo hacer uso del operador de fusión nula (??).?..==realmente funcionará. Ya sé lo que sucede con un booleano intuitivamente cuando solo puede sertrueofalse; Ni siquiera tengo que pensarlo. Pero tírelonulla la mezcla, y ahora tengo que averiguar si eso==es correcto.??en su lugar, simplemente elimina elnullcaso, reduciéndolo de nuevo atrueyfalse, 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.
nullsignifica algo muy diferente deEnumerable.Empty<T>Como se menciona en la respuesta de Erik Eidt , hay una diferencia de significado entre
nully 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 .nullSin 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
nully vacío puede resultar en el mismo valor de retorno para la función, pero eso no significa quenully vacío significa exactamente lo mismo en el alcance de la llamada.Hay muchas respuestas que explican por qué
nully 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.nullobjeto 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
nulles 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
returndeclaració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.IEnumerableySystem.Collections.Generic.IEnumerable<T>, tanto en sus colecciones como enforeachbucles. Estas clases volvieronIEnumeratorobjetos que sólo tenían los dos métodosCurrentyMoveNext, 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 deCurrentyMoveNextcon relativa facilidad), la lanzaron como métodos de extensión, que se pueden aplicar a cualquier sistema existente actualmente.IEnumerableinstancia 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 instanciaIEnumerabley 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