Es bastante molesto probar todas mis cadenas null
antes de poder aplicar de forma segura métodos como ToUpper()
, StartWith()
etc.
Si el valor predeterminado de string
fuera la cadena vacía, no tendría que probar, y sentiría que es más consistente 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
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 él.
c#
string
default-value
Marcel
fuente
fuente
null
(y también le recomiendo que trate conceptualmentenull
y vacíe las cadenas como cosas diferentes). Un valor nulo podría ser el resultado de un error en alguna parte, mientras que una cadena vacía debe transmitir un significado diferente.Respuestas:
Porque
string
es un tipo de referencia y el valor predeterminado para todos los tipos de referencia esnull
.Eso es consistente con el comportamiento de los tipos de referencia. Antes de invocar a los miembros de su instancia, se debe establecer una marca para una referencia nula.
Asignar el valor predeterminado a un tipo de referencia específico que
null
no sea inconsistente .Nullable<T>
trabaja con los tipos de valor. Cabe destacar el hecho de queNullable
no se introdujo en la plataforma .NET original , por lo que habría habido un montón de código roto si hubieran cambiado esa regla. ( Cortesía de @jcolebrand )fuente
String
, excepto por (1) el comportamiento de tipo de valor de tener un valor predeterminado utilizable, y (2) una desafortunada capa adicional de indirección de boxeo cada vez que se lanzó aObject
. Dado que la representación del montónstring
es única, tener un tratamiento especial para evitar el boxeo adicional no habría sido muy difícil (en realidad, poder especificar comportamientos de boxeo no predeterminados también sería algo bueno para otros tipos).Habib tiene razón, porque
string
es un tipo de referencia.Pero lo más importante, no tiene que verificar
null
cada vez que lo usa. Sin embargo, probablemente debería arrojar unArgumentNullException
si alguien pasa su función comonull
referencia.Aquí está la cosa: el marco arrojaría un
NullReferenceException
para usted de todos modos si intenta llamar.ToUpper()
a una cadena. Recuerde que este caso aún puede ocurrir incluso si prueba sus argumentosnull
ya que cualquier propiedad o método en los objetos pasados a su función como parámetros pueden evaluarnull
.Dicho esto, la verificación de cadenas vacías o nulos es algo común, por lo que proporcionan
String.IsNullOrEmpty()
yString.IsNullOrWhiteSpace()
para este propósito.fuente
NullReferenceException
usted mismo ( msdn.microsoft.com/en-us/library/ms173163.aspx ); arroja unArgumentNullException
si su método no puede aceptar referencias nulas. Además, los NullRef suelen ser una de las excepciones más difíciles de diagnosticar cuando se solucionan problemas, por lo que no creo que la recomendación de no verificar null sea muy buena.ArgumentNullException
tiene el beneficio adicional de poder proporcionar el nombre del parámetro. Durante la depuración, esto ahorra ... err, segundos. Pero segundos importantes.Podría escribir un método de extensión (para lo que vale):
Ahora esto funciona de manera segura:
fuente
.EmptyNull()
, ¿por qué no simplemente usarlo(str ?? "")
donde sea necesario? Dicho esto, estoy de acuerdo con el sentimiento expresado en el comentario de @ DaveMarkle: probablemente no deberías.null
yString.Empty
son conceptualmente diferentes, y no necesariamente puedes tratar a uno igual que a otro.También puede usar lo siguiente, a partir de C # 6.0
El resultado de la cadena será nulo.
fuente
public string Name { get; set; } = string.Empty;
Las cadenas vacías y los valores nulos son fundamentalmente diferentes. Un valor 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á formas de validar si ha pasado intencionalmente un valor en blanco o si ha olvidado completar el valor de esa variable.
Otra ocasión en la que esto sería un problema es cuando la cadena es un valor de retorno de alguna función. Como string es un tipo de referencia y técnicamente puede tener un valor como nulo y vacío, por lo tanto, la función también puede devolver técnicamente un valor nulo o vacío (no hay nada que lo detenga). Ahora, dado que hay 2 nociones de la "ausencia de un valor", es decir, una cadena vacía y un valor nulo, todo el código que consume esta función tendrá que hacer 2 comprobaciones. Uno para vacío y 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.
/software/32578/sql-empty-string-vs-null-value
NULL vs Empty cuando se trata de la entrada del usuario
fuente
null
representaría la cadena vacía. Hay varias maneras en que .net podría haber implementado una semántica similar, si hubieran elegido hacerlo, especialmente dado queString
tiene una serie de características que lo convierten en un tipo único de todos modos [por ejemplo, y los dos tipos de matriz son los únicos tipos cuya asignación el tamaño no es constante].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 pudieran especificar que deben ser llamados directamente, en lugar de vía
callvirt
, sin que la persona que realiza la llamada realice un verificación de referencia nula; Tampoco proporcionó un significado de estructuras definitorias que no estarían sujetas al boxeo "normal".Si la especificación CLS hubiera definido dicho medio, entonces sería posible que .net siguiera consistentemente el liderazgo establecido por el Modelo de Objeto Común (COM), bajo el cual una referencia de cadena nula se consideraba semánticamente equivalente a una cadena vacía, y para otros tipos de clase inmutables definidos por el usuario que se supone que tienen una semántica de valores para definir igualmente los valores predeterminados. Esencialmente, lo que sucedería sería que cada miembro de
String
, por ejemplo,Length
se escribiera como algo así[InvokableOnNull()] int String Length { get { if (this==null) return 0; else return _Length;} }
. Este enfoque habría 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 estos tipos yObject
podría volverse un poco turbia.Un enfoque alternativo habría sido permitir la definición de tipos de estructura especiales que no heredaran,
Object
sino que tuvieran operaciones de boxeo y desempaquetado personalizadas (que se convertirían a / de algún otro tipo de clase). Bajo tal enfoque, habría un tipo de claseNullableString
que se comporta como lo hace una cadena ahora, y un tipo de estructura en caja personalizadaString
, que contendría un único campoValue
de tipo privadoString
. El intento de convertir unString
aNullableString
oObject
volveríaValue
si no nulo, oString.Empty
si es nulo. Intentando convertirString
, una referencia no nula a unaNullableString
instancia almacenaría la referencia enValue
(tal vez almacenando nulo si la longitud fuera cero); emitir cualquier otra referencia arrojaría una excepción.Aunque las cadenas deben almacenarse en el montón, conceptualmente no hay ninguna razón por la que no deberían comportarse como tipos de valor que tienen un valor predeterminado no nulo. Tenerlos almacenados como una estructura "normal" que contenía una referencia habría sido eficiente para el código que los usó como tipo "cadena", pero habría agregado una capa adicional de indirección e ineficiencia al convertir a "objeto". Si bien no preveo 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.
fuente
Nullable<>
, las cadenas podrían volver a implementarse como tipos de valor; No puedo hablar de los costos y beneficios de tal elección.string
es más eficiente deNullable<string>
lo que sería, tener que usar el método "más eficiente" es más oneroso que poder usar el mismo enfoque para todos los valores de bases de datos anulables.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.
fuente
string
que deba ser un tipo de referencia. Para estar seguros, los caracteres reales que componen la cadena ciertamente deben almacenarse en el montón, pero dada la cantidad de soporte dedicado que las cadenas ya tienen en el CLR, no sería una exageración tenerSystem.String
un tipo de valor con un Campo privado únicoValue
de tipoHeapString
. Ese campo sería un tipo de referencia, y sería predeterminadonull
, pero unaString
estructura cuyoValue
campo era nulo se comportaría como una cadena vacía. La única desventaja de este enfoque sería ...String
to aObject
, en ausencia de un código de caso especial en el tiempo de ejecución, provocaría la creación de unaString
instancia en caja en el montón, en lugar de simplemente copiar una referencia alHeapString
..Length
etc., de modo que las instancias que se mantienen una referencia nula no intentaría desreferenciarla, sino que se comportaría como corresponde para una cadena vacía. Si el Marco sería mejor o peor constring
implementada de esa manera, si uno queríadefault(string)
ser una cadena vacía ...string
un envoltorio de tipo de valor en un campo de tipo de referencia sería el enfoque que requeriría la menor cantidad de cambios a otras partes de .net [de hecho, si uno estuviera dispuesto a aceptar la conversiónString
paraObject
crear un elemento en caja adicional, uno podría simplementeString
ser una estructura ordinaria con un campo de tipoChar[]
que nunca expuso]. Creo que tener unHeapString
tipo probablemente sería mejor, pero de alguna manera la cadena de tipo de valor que contiene unChar[]
sería más simple.Como 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.Usemos la
default
palabra clave para este caso;str
es unstring
, entonces es un tipo de referencia , entonces el valor predeterminado esnull
.str
es unint
, entonces es un tipo de valor , entonces el valor predeterminado eszero
.fuente
¡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
.Punto verdadero Tendría más sentido no permitir
null
ningún tipo de referencia, sino que requeriríaNullable<TheRefType>
esa característica.Consistencia con otros tipos de referencia. Ahora, ¿por qué permitir los
null
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 lenguaje que también proporcionaNullable
.fuente
Quizás si usa el
??
operador al asignar su variable de cadena, podría ayudarlo.fuente
Una cadena es un objeto inmutable, lo que significa que cuando se le da un valor, el valor anterior 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. Entonces, si el valor predeterminado de
String a
fueString.Empty
, desperdiciaría elString.Empty
bloque en la memoria cuando se le dio 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 podría usar laStringBuilder
clase mutable si esto fuera un problema.fuente
String.Empty
. ¿Estoy equivocado?string
sea una cadena vacía es permitir que todos los bits cero sean una representación de una cadena vacía. Hay varias maneras en que esto se podría lograr, pero no creo que ninguna implique la inicialización de referenciasString.Empty
.String.Empty
o""
.string
las ubicaciones de almacenamiento habría requerido también cambiar el comportamiento de inicialización de todas las estructuras o clases que tienen campos de tipostring
. Eso representaría un cambio bastante grande en el diseño de .net, que actualmente espera inicializar a cero cualquier tipo sin siquiera tener que pensar en qué es, salvo solo por su tamaño total.Como la cadena es un tipo de referencia y el valor predeterminado para el tipo de referencia es nulo.
fuente
Tal vez la
string
palabra clave lo confundió, ya que se ve exactamente como cualquier otra declaración de tipo de valor , pero en realidad es un aliasSystem.String
como se explica en esta pregunta .Además, el color azul oscuro en Visual Studio y la primera letra minúscula pueden inducir a error al pensar que es un
struct
.fuente
object
palabra clave? Aunque es cierto, eso es mucho menos utilizado questring
.int
es un alias paraSystem.Int32
. ¿Cual es tu punto? :)System.Int32
es unStruct
teniendo así un valor por defecto, mientrasSystem.String
es unaClass
que tiene un puntero con el valor por defecto denull
. Se presentan visualmente en la misma fuente / color. Sin conocimiento, uno puede pensar que actúan de la misma manera (= tener un valor predeterminado). Mi respuesta fue escrita con una idea de psicología cognitiva en.wikipedia.org/wiki/Cognitive_psychology detrás de ella :-)Los tipos anulables no llegaron hasta 2.0.
Si los tipos anulables se hubieran hecho al comienzo del lenguaje, entonces string no hubiera sido anulable y string? habría sido anulable. Pero no pudieron hacer esto du a la compatibilidad con versiones anteriores.
Mucha gente habla de ref-type o no ref type, pero string es una clase fuera de lo común y se habrían encontrado soluciones para hacerlo posible.
fuente