2009-04-26 10 views
11

Todo el mundo conoce y ama el método String.IsNullOrEmpty (yourString).¿Extender la clase String con IsNullOrEmpty es confuso?

Me preguntaba si se va a confundir a los desarrolladores de código o hacer mejor si extendemos clase String tener método como este:

yourString.IsNullOrEmpty(); 

Pro:

  1. más legible.
  2. Menos tipeo.

Contras:

  1. puede ser confuso porque yourString variable puede ser null y parece como que está ejecutando el método en una variable null.

¿Qué opinas?

La misma pregunta que podemos hacer acerca del método myObject.IsNull().

Que cómo lo escribiría:

public static class StringExt 
{ 
    public static bool IsNullOrEmpty(this string text) 
    { 
    return string.IsNullOrEmpty(text); 
    } 

    public static bool IsNull(this object obj) 
    { 
    return obj == null; 
    } 
} 

Respuesta

6

Personalmente, no soy partidario de hacer esto. El mayor problema con los métodos de extensión en este momento es la capacidad de descubrimiento. Una vez que conoce todos los métodos que existen en un tipo particular, no es posible mirar una llamada a método y saber que es un método de extensión. Como tal, me resulta problemático hacer cualquier cosa con un método de extensión que no sería posible con una llamada a método normal. De lo contrario, acabarás confundiendo a los desarrolladores.

Existe un corolario a este problema en C++. En varias implementaciones de C++, es posible invocar métodos de instancia en punteros NULL siempre que no toque ningún campo o método virtual en el tipo. He trabajado con varios códigos que hacen esto intencionalmente y dan a los métodos un comportamiento diferente cuando "this==NULL". Es bastante enloquecedor trabajar con él.

Esto no quiere decir que no me gustan los métodos de extensión. Muy por el contrario, los disfruto y los uso con frecuencia. Pero creo que hay 2 reglas importantes que debes seguir al escribirlas.

  • Trate la implementación del método real como si fuera simplemente otro método estático porque de hecho lo es.Por ejemplo lanzar ArgumentException en lugar de NullReference excepción para un null este
  • No permita que un método de extensión realizar trucos que un método de instancia normal no podría hacer

EDITAR En respuesta al comentario de Mehrdad

El problema de aprovecharlo es que no veo que str.IsNullOrEmpty tenga una ventaja funcional significativa sobre String.IsNullOrEmpty (str). La única ventaja que veo es que uno requiere menos tipeo que el otro. Lo mismo podría decirse sobre los métodos de extensión en general. Pero en este caso también está alterando la forma en que las personas piensan sobre el flujo de programas.

Si la tipificación más corta es lo que la gente realmente quiere, ¿no sería IsNullOrEmpty (str) una opción mucho mejor? Es ambiguo y es el más corto de todos. True C# no tiene soporte para tal característica hoy. Pero imagina si en C# podría decir

using SomeNamespace.SomeStaticClass; 

El resultado de esto es que todos los métodos de SomeStaticClass estaban ahora en el espacio de nombres global y disponible para la unión. Esto parece ser lo que la gente quiere, pero lo están asociando a un método de extensión del que no soy un gran admirador.

+0

Estoy de acuerdo con su primer punto, pero no del todo el segundo. Creo que hay un problema de inconsistencia que tenemos que enfrentar y no podemos hacer nada al respecto. Podríamos aprender a gustar y, al menos, aprovechar las ventajas (a la vez que tenemos una idea de lo que realmente está sucediendo). Por cierto, hay un problema de inconsistencia similar en 'Nullable .Equals()' cuando se llama en 'null'. –

+0

Sí nullable hace trucos como este hoy. Pero mira cuánta gente confunde. Parece que tenemos una pregunta emergente aquí una vez a la semana sobre "por qué los elementos nullables funcionan de forma divertida con operaciones booleanas si un valor es nulo". Voy a responder a la primera parte con una edición. Se agotó el espacio en el campo de comentarios – JaredPar

+0

¡LOL! Este Nullable estaba condenado porque cambió el comportamiento en beta. ¡Creo que arruinó el equipo de SQL/CLR en ese momento! No estoy hablando del método 'String.IsNullOrEmpty' en particular. Todos los métodos que podrían funcionar con 'null' son aceptables y tienen sentido como un buen método de extensión. Mi opinión es que deberíamos aceptar que la sintaxis '.Method()' es más que una simple llamada al método de instancia. Por ejemplo, podríamos tener el método XmlAttribute.ValueEquals ("xyz") para eludir la comprobación nula en LINQ to XML. No lo sé. Estoy pensando en tu punto. Por cierto, odio esa cosa global de "usar". –

5

Me gusta mucho este enfoque, siempre que el método deja claro que quiere comprobar si el objeto es nulo. ThrowIfNull, IsNull, IsNullOrEmpty, etc. Es muy legible.

8

creo que la raíz de este problema es lo que Jon Skeet ha mencionado en la lista de cosas que hates in his favorite language (C#): C# no debería haber importado todos los métodos de extensión en su conjunto espacio de nombres de forma automática. Este proceso debería haberse hecho más explícitamente.

Mi opinión personal sobre esta pregunta específica es (ya que no podemos hacer nada sobre el hecho anterior) utilizar el método de extensión si lo desea. No digo que no sea confuso, pero este hecho sobre los métodos de extensión (que se puede invocar en las referencias null) es algo global y no afecta solo a String.IsNullOrEmpty, por lo que los desarrolladores de C# deberían familiarizarse con él.

Por cierto, es una suerte que Visual Studio identifique claramente los métodos de extensión por (extension) en la información sobre herramientas de IntelliSense.

+0

La pregunta se vincula a que se ha eliminado – Firedragon

+0

@Firedragon se parece a una versión comprimida aún está disponible aquí: http: // www. stackprinter.com/questions/what-are-five-things-you-hate-about-your-favorite-language.html – Matthias

+0

La [pregunta] (http://stackoverflow.com/q/282329/1497596) que inspiró la [respuesta referenciada] (http://stackoverflow.com/a/282342/1497596) ha sido restaurada pero está cerrada. – DavidRR

0

Creo que extender cualquier objeto con una llamada tipo "IsNull" es un poco confuso y se debe evitar si es posible.

Es discutible que el método IsEmptyString podría ser útil para el tipo de Cadena, y eso porque normalmente combinaría esto con una prueba de nulo que IsNullOrEmpty podría ser útil, pero evitaría esto también debido al hecho que el tipo de cadena ya tiene un método estático que hace esto y no estoy seguro de que te estés ahorrando esa cantidad de tipeo (5 caracteres como máximo).

4

Personalmente, no crearía una extensión que hace algo que ya existe en el marco a menos que sea una mejora significativa en la usabilidad. En este caso, no creo que ese sea el caso.Además, si tuviera que crear una extensión, la nombraría de una manera que reduzca la confusión, no la aumente. De nuevo, creo que este caso falla esa prueba.

Habiendo dicho todo eso, tengo una extensión de cadena que prueba, no solo si la cadena es nula o está vacía, sino también si solo contiene espacios en blanco. Lo llamo IsNothing. Lo puedes encontrar here.

1

Parece que no está llamando a un método en una variable nula. Usted/está/llamando a un método en una variable nula, aunque una implementada a través de un método de extensión estático. No tenía idea de los métodos de extensión (que ya desconfiaba). Esto incluso le permite hacer cosas locas como:

public static int PowerLength(this string obj) 
{ 
return obj == null ? 0 : obj.Length; 
} 

Desde donde estoy ahora, que yo clasificaría cualquier uso de un método de extensión en una referencia nula en virtud de considerada perjudicial.

+0

Curiosamente, consideraría que la función de longitud tiene la semántica que String.Length debería haber tenido desde el principio, especialmente dado que bajo COM, y en muchos lenguajes antes de .net, el valor predeterminado para una variable de cadena se puede usar como un vacío cuerda. Tal como está, un valor nulo se interpreta como una cadena vacía en algunos contextos, pero no en otros, lo cual es una situación mucho más confusa que la que existiría si los objetos nulos que se * tipificaron * como cadenas se comportaron como cadenas vacías. – supercat

0

Llamar a un método en una variable que es nula generalmente da como resultado una NullReferenceException. El IsNullOrEmpty() -Método se desvía de este comportamiento de una manera que no es predecible al solo mirar el código. Por lo tanto, desaconsejaría usarlo, ya que crea confusión y el beneficio de guardar un par de caracteres es mínimo.

1

Sí, confundirá.

Creo que todos los que conocen IsNullOrEmpty() perciben el uso de esto como bastante natural. Por lo tanto, su método de extensión sugerido no agregará más legibilidad.

Quizás para alguien nuevo en .NET este método de extensión podría ser más fácil de tratar, pero existe la posibilidad de peligro de que él/ella no entienda todas las facetas de los métodos de extensión (como que necesita importar espacio de nombres y que puede invocarse en nulo). Él/ella podría preguntarse por qué este método de extensión no existe en otro proyecto. De todos modos: incluso si alguien es nuevo en .NET, la sintaxis del método IsNullOrEmpty() podría volverse natural bastante rápido. También aquí los beneficios de los métodos de extensión no superan la confusión causada por ellos.

Editar: Intenté reformular lo que quería decir.

+0

No creo que podamos decir "él/ella no entiende todas las facetas de los métodos de extensión", así que no lo use.Si seguimos esta filosofía, podemos empezar a utilizar solo for loop porque alguien puede no entender el ciclo foreach. Como desarrollador, mi trabajo es conocer el idioma con el que trabajo. No usarás un mecánico que no sepa cómo usar sus herramientas. – Vadim

+0

Mi intención no era difundir la filosofía aquí. Hipotéticamente, alguien nuevo en el lenguaje puede encontrar que la sintaxis del método de extensión es más natural, pero eso puede no durar mucho, cuando él o ella descubren cómo funciona realmente. Eso es todo lo que quería decir. –

0

En general, estoy bien con los métodos de extensión que son seguros para invocar null si tienen la palabra 'null' o algo así en su nombre. De esa forma, estoy al tanto del hecho de que es seguro llamar con nulo. Además, es mejor que documenten ese hecho en el encabezado de comentario XML, así que obtengo esa información cuando paso el mouse sobre la llamada.

1

Veamos los pros: s y en contra: s ...

  1. más legible.

Sí, un poco, pero la mejora de la legibilidad se ve compensado por el hecho de que parece que está llamando a un método de instancia en algo que no tiene por qué ser una instancia. En efecto, es más fácil de leer, pero es más difícil de entender, por lo que la legibilidad mejorada es solo una ilusión.

  1. Menos tipeo.

Sí, pero eso no es realmente un argumento fuerte. Si escribir es la parte principal de su programación, simplemente no está haciendo algo que sea remotamente lo suficientemente desafiante como para que pueda evolucionar como desarrollador.

  1. puede ser confuso porque la variable SuCadena puede ser nulo y parece que que está ejecutando el método en una variable nula.

Verdadero, (como se mencionó anteriormente).

+1

Creo que la pregunta principal es si filosóficamente uno considera' BobsStringUtilities.IsNullOrShorterThan (string st, int minLength) 'estar más asociado con' BobsStringUtilities' o con 'string'. En algunos casos, especificar un nombre de clase antes de una función estática mejora la legibilidad, pero en muchos otros casos es realmente solo ruido. – supercat

19

Si no me equivoco, cada respuesta aquí denigra el hecho de que el método de extensión se puede invocar en una instancia nula, y debido a esto no admiten creer que es una buena idea.

Déjenme contar sus argumentos.

No creo EN ABSOLUTO que llamar a un método sobre un objeto que puede ser nulo es confuso. El hecho es que solo buscamos valores nulos en ciertas ubicaciones, y no el 100% del tiempo. Eso significa que hay un porcentaje de tiempo en el que cada llamada a un método que hacemos es potencialmente sobre un objeto nulo. Esto es entendido y aceptable. Si no fuera así, estaríamos verificando el nulo antes de cada llamada a un solo método.

Entonces, ¿cómo es confuso que una llamada de método en particular pueda estar ocurriendo en un objeto nulo? Mira el siguiente código:

var bar = foo.DoSomethingResultingInBar(); 
Console.Writeline(bar.ToStringOr("[null]")); 

¿Estás confundido? Usted debería ser. Porque aquí está la implementación de foo:

public Bar DoSomethingResultingInBar() 
{ 
    return null; //LOL SUCKER! 
} 

Ver? Lees la muestra del código sin confundirte en absoluto. Usted entendió que, potencialmente, foo devolvería un nulo de esa llamada a método y la llamada a ToStringOr en bar resultaría en un NRE. ¿Tu cabeza dio vueltas? Por supuesto no. Se entiende que esto puede suceder. Ahora, ese método ToStringOr no es familiar. ¿Qué haces en estas situaciones? O lee los documentos sobre el método o examina el código de la llamada. Aquí está:

public static class BarExtensions 
{ 
    public static string ToStringOr(this bar, string whenNull) 
    { 
    return bar == null ? whenNull ?? "[null]" : bar.ToString(); 
    } 
} 

Confusing? Por supuesto no. Es obvio que el desarrollador quería un método abreviado para comprobar si bar es nulo y sustituirlo por una cadena no nula. Hacer esto puede reducir drásticamente el código y aumentar la legibilidad y la reutilización del código. Por supuesto, podrías hacer esto de otras maneras, pero de esta manera no sería más confuso que cualquier otro. Por ejemplo:

var bar = foo.DoSomethingResultingInBar(); 
Console.Writeline(ToStringOr(bar, "[null]")); 

Cuando se encuentra con este código, ¿qué tiene que diferenciar de la versión original? Todavía tiene que examinar el código, aún debe determinar su comportamiento cuando bar es nulo. Aún tienes que lidiar con esta posibilidad.

¿Los métodos de extensión son confusos? Solo si no los entiendes Y, francamente, lo mismo se puede decir de CUALQUIER parte del lenguaje, desde delegados hasta lambdas.

+0

La forma correcta de escribir esto, si .net lo hubiera permitido, hubiera sido utilizar un método de instancia en 'barra' y decorarlo de alguna manera para indicar al compilador * no * que use 'callvirt'. Considero que el enfoque del método de extensión es una solución fea para lo que debería ser específico directamente. – supercat

+0

@supercat: no es una "solución alternativa", necesitaban implementarlo de tal manera que sea compatible con todos los lenguajes .NET (y cualquier otro CLS que cumpla). – Will

+0

Mi punto es que la especificación de CLS debería haber incluido una forma de marcar los métodos que deberían invocarse en instancias nulas (y, por cierto, marcar qué métodos de estructura y propiedades modifican 'this'). Si la especificación incluyera esas cosas, una gran cantidad de código podría haber sido mucho más limpia. – supercat

0

Al comparar la instancia de clase con null usando ".IsNull()" no es incluso más corto que con "== null". Las cosas cambian en clases genéricas cuando el argumento de tipo genérico sin restricción puede ser un tipo de valor. En tal caso la comparación con el tipo de defecto es muy largo y que utilizan la extensión a continuación:

public static bool IsDefault<T>(this T x) 
    { 
     return EqualityComparer<T>.Default.Equals(x, default(T)); 
    }