c# string default-value

c# - ¿Por qué el valor predeterminado del tipo de cadena es nulo en lugar de una cadena vacía?



default c# (14)

¿Por qué el valor predeterminado del tipo de cadena es nulo en lugar de una cadena vacía?

Debido a que la string es un tipo de referencia y el valor predeterminado para todos los tipos de referencia es null .

Es bastante molesto probar todas mis cadenas para null antes de que pueda aplicar con seguridad métodos como ToUpper (), StartWith (), etc.

Eso es consistente con el comportamiento de los tipos de referencia. Antes de invocar a los miembros de su instancia, uno debe poner un cheque en el lugar para una referencia nula.

Si el valor predeterminado de la cadena fuera la cadena vacía, no tendría que probar, y sentiría que es más coherente con los otros tipos de valores como int o double, por ejemplo.

Asignar el valor predeterminado a un tipo de referencia específico que no sea null lo haría inconsistente .

Además, Nullable<String> tendría sentido.

Nullable<T> funciona con los tipos de valor. Cabe destacar el hecho de que Nullable no se introdujo en la plataforma .NET original , por lo que habría habido muchos códigos rotos si hubieran cambiado esa regla ( Cortesía @jcolebrand )

Es bastante molesto probar todas mis cadenas para null antes de que pueda aplicar con seguridad métodos como ToUpper() , StartWith() etc.

Si el valor predeterminado de la string fuera la cadena vacía, no tendría que probar, y sentiría que es más coherente con los otros tipos de valores como int o double por ejemplo. Además, Nullable<String> tendría sentido.

Entonces, ¿por qué los diseñadores de C # eligieron usar null como el valor predeterminado de las cadenas?

Nota: Esto se relaciona con esta pregunta , pero se centra más en el por qué en lugar de qué hacer con ella.


¿Por qué los diseñadores de C # eligieron usar null como el valor predeterminado de las cadenas?

Dado que las cadenas son tipos de referencia , los tipos de referencia tienen un valor predeterminado null . Las variables de los tipos de referencia almacenan referencias a los datos reales.

Vamos a utilizar la palabra clave default para este caso;

string str = default(string);

str es una string , por lo que es un tipo de referencia , por lo que el valor predeterminado es null .

int str = (default)(int);

str es un int , por lo que es un tipo de valor , por lo que el valor predeterminado es zero .


Si el valor predeterminado de la string fuera la cadena vacía, no tendría que probar

¡Incorrecto! Cambiar el valor predeterminado no cambia el hecho de que es un tipo de referencia y alguien aún puede establecer explícitamente que la referencia sea null .

Además, Nullable<String> tendría sentido.

Punto verdadero. Tendría más sentido no permitir el null para ningún tipo de referencia, en lugar de ello, se requiere Nullable<TheRefType> para esa característica.

Entonces, ¿por qué los diseñadores de C # eligieron usar null como el valor predeterminado de las cadenas?

Coherencia con otros tipos de referencia. Ahora, ¿por qué permitir null en los tipos de referencia? Probablemente para que se sienta como C, a pesar de que esta es una decisión de diseño cuestionable en un idioma que también proporciona Nullable .


Dado que la cadena es un tipo de referencia y el valor predeterminado para el tipo de referencia es nulo.


Habib tiene razón, porque la string es un tipo de referencia.

Pero, lo que es más importante, no es necesario comprobar si hay un null cada vez que se usa. Sin embargo, probablemente debería lanzar una ArgumentNullException si alguien pasa su función a una referencia null .

Aquí está la cosa: el marco lanzaría una NullReferenceException para usted de todos modos si intentara llamar a .ToUpper() en una cadena. Recuerde que este caso aún puede ocurrir incluso si prueba sus argumentos para null ya que cualquier propiedad o método en los objetos pasados ​​a su función como parámetros puede evaluar a null .

Dicho esto, la comprobación de cadenas vacías o nulos es algo común, por lo que proporcionan String.IsNullOrEmpty() y String.IsNullOrWhiteSpace() solo para este propósito.


La razón / problema fundamental es que los diseñadores de la especificación CLS (que define cómo interactúan los lenguajes con .net) no definieron un medio por el cual los miembros de la clase podrían especificar que deben llamarse directamente, en lugar de a través de callvirt , sin que el llamante realice una comprobación de referencia nula; tampoco proporcionó un significado de definir estructuras que no estarían sujetas al boxeo "normal".

Si la especificación de CLS hubiera definido tales medios, sería posible que .net siguiera de manera consistente el liderazgo establecido por el Modelo de objetos comunes (COM), bajo el cual una referencia de cadena nula se consideraba semánticamente equivalente a una cadena vacía, y para otras Tipos de clase inmutables definidos por el usuario que se supone que tienen semántica de valor para definir igualmente valores por defecto. Esencialmente, lo que sucedería sería que cada miembro de String , por ejemplo, la Length se escriba como algo como [InvokableOnNull()] int String Length { get { if (this==null) return 0; else return _Length;} } [InvokableOnNull()] int String Length { get { if (this==null) return 0; else return _Length;} } . Este enfoque hubiera ofrecido una semántica muy buena para cosas que deberían comportarse como valores, pero debido a problemas de implementación, deben almacenarse en el montón. La mayor dificultad con este enfoque es que la semántica de conversión entre tales tipos y Object podría volverse un poco turbia.

Un enfoque alternativo hubiera sido permitir la definición de tipos de estructuras especiales que no heredaron de Object sino que tenían operaciones de boxeo y desempaquetado personalizadas (que se convertirían a / desde algún otro tipo de clase). Bajo tal enfoque, habría un tipo de clase NullableString que se comporta como lo hace la cadena ahora, y una String tipo de estructura personalizada, que contendría un solo campo privado Value del tipo String . Intentar convertir una String a NullableString u Object devolvería un Value si no es nulo, o String.Empty si es nulo. Al intentar convertir a String , una referencia no nula a una instancia de NullableString almacenaría la referencia en Value (tal vez almacenando nulo si la longitud fuera cero); lanzar cualquier otra referencia arrojaría una excepción.

A pesar de que las cadenas deben almacenarse en el montón, conceptualmente no hay razón por la que no deban comportarse como tipos de valor que tienen un valor predeterminado no nulo. Tenerlos almacenados como una estructura "normal" que contenía una referencia hubiera sido eficiente para el código que los usó como tipo "cadena", pero habría agregado una capa adicional de indirección e ineficiencia cuando se convierte en "objeto". Si bien no creo que .net agregue ninguna de las características anteriores en esta fecha tardía, quizás los diseñadores de futuros marcos podrían considerar incluirlas.


Las cadenas vacías y los nulos son fundamentalmente diferentes. Un nulo es la ausencia de un valor y una cadena vacía es un valor que está vacío.

El lenguaje de programación que hace suposiciones sobre el "valor" de una variable, en este caso una cadena vacía, será tan bueno como iniciar la cadena con cualquier otro valor que no cause un problema de referencia nulo.

Además, si pasa el identificador a esa variable de cadena a otras partes de la aplicación, entonces ese código no tendrá forma de validar si ha pasado intencionalmente un valor en blanco o si ha olvidado rellenar el valor de esa variable.

Otra ocasión en la que esto podría ser un problema es cuando la cadena es un valor de retorno de alguna función. Dado que la cadena es un tipo de referencia y técnicamente puede tener un valor como nulo y vacío ambos, por lo tanto, la función también puede técnicamente devolver un nulo o vacío (no hay nada que impida que lo haga). Ahora, ya que hay 2 nociones de la "ausencia de un valor", es decir, una cadena vacía y una nula, todo el código que consume esta función tendrá que realizar 2 comprobaciones. Uno para vacío y el otro para nulo.

En resumen, siempre es bueno tener solo 1 representación para un solo estado. Para una discusión más amplia sobre vacíos y nulos, vea los enlaces a continuación.

https://softwareengineering.stackexchange.com/questions/32578/sql-empty-string-vs-null-value

NULL vs vacío cuando se trata de la entrada del usuario


Los tipos anulables no llegaron hasta el 2.0.

Si los tipos anulables se hubieran hecho al principio del lenguaje, ¿la cadena habría sido no anulable y la cadena? habría sido nullable. Pero no pudieron hacer esto por compatibilidad con versiones anteriores.

Mucha gente habla de ref-type o no ref type, pero la cadena es una clase fuera de lo común y se habría encontrado que las soluciones lo hacen posible.


Podrías escribir un método de extensión (por lo que vale):

public static string EmptyNull(this string str) { return str ?? ""; }

Ahora esto funciona con seguridad:

string str = null; string upper = str.EmptyNull().ToUpper();


Porque una variable de cadena es una referencia , no una instancia .

Inicializarlo a Vacío por defecto habría sido posible, pero habría introducido muchas inconsistencias en todo el tablero.


Tal vez la palabra clave de string confundido, ya que se ve exactamente como cualquier otra declaración de tipo de valor , pero en realidad es un alias para System.String como se explica en esta pregunta .
También el color azul oscuro en Visual Studio y la primera letra minúscula pueden inducir a error al pensar que es una struct .


Tal vez si lo usarías ?? operador al asignar su variable de cadena, podría ayudarle.

string str = SomeMethodThatReturnsaString() ?? ""; // if SomeMethodThatReturnsaString() returns a null value, "" is assigned to str.


También podría (a partir de VS2015 y el nuevo compilador) usar lo siguiente

string myString = null; string result = myString?.ToUpper();

El resultado de la cadena será nulo.


Una cadena es un objeto inmutable que significa que cuando se le asigna un valor, el valor antiguo no se borra de la memoria, sino que permanece en la ubicación anterior, y el nuevo valor se coloca en una nueva ubicación. Por lo tanto, si el valor predeterminado de String a era String.Empty , perdería el bloque String.Empty en la memoria cuando se le String.Empty su primer valor.

Aunque parece minúsculo, podría convertirse en un problema al inicializar una gran variedad de cadenas con valores predeterminados de String.Empty . Por supuesto, siempre se podría usar la clase mutable StringBuilder si esto fuera un problema.