É muito chato testar todas as minhas strings null
antes que eu possa aplicar com segurança métodos como ToUpper()
, StartWith()
etc ...
Se o valor padrão de string
fosse a sequência vazia, eu não precisaria testar e sentiria que era mais consistente com os outros tipos de valor, como int
ou double
por exemplo. Além disso Nullable<String>
, faria sentido.
Então, por que os designers de C # optaram por usar null
como o valor padrão de strings?
Nota: Isso está relacionado a esta questão , mas é mais focado no porquê, em vez do que fazer com ele.
c#
string
default-value
Marcel
fonte
fonte
null
(e também recomendo que você trate conceitualmentenull
e esvazie as strings como coisas diferentes). Um valor nulo pode ser o resultado de um erro em algum lugar, enquanto uma string vazia deve transmitir um significado diferente.Respostas:
Porque
string
é um tipo de referência e o valor padrão para todos os tipos de referência énull
.Isso é consistente com o comportamento dos tipos de referência. Antes de chamar os membros da instância, deve-se colocar uma verificação para uma referência nula.
Atribuir o valor padrão a um tipo de referência específico que
null
não o tornaria inconsistente .Nullable<T>
trabalha com os tipos de valor. É digno de nota o fato de queNullable
não foi introduzido na plataforma .NET original; portanto, haveria muito código quebrado se eles alterassem essa regra. ( Cortesia: @jcolebrand )fonte
String
, exceto por (1) o comportamento de valor-tipo-ish de ter um valor padrão utilizável e (2) uma infeliz camada extra de indireção de boxe a qualquer momento em que foi lançada.Object
. Dado que a representação de heap destring
é única, ter tratamento especial para evitar boxe extra não seria muito exagerado (na verdade, ser capaz de especificar comportamentos de boxe não padrão também seria bom para outros tipos).Habib está certo - porque
string
é um tipo de referência.Mais importante, porém, você não precisa verificar
null
cada vez que o usa. Você provavelmente deve jogar aArgumentNullException
se alguém passar umanull
referência à sua função .Aqui está a coisa - a estrutura lançaria um
NullReferenceException
para você de qualquer maneira, se você tentasse chamar.ToUpper()
uma string. Lembre-se de que esse caso ainda pode ocorrer mesmo se você testar seus argumentos,null
pois qualquer propriedade ou método nos objetos passados para sua função como parâmetros podem ser avaliadosnull
.Dito isto, verificar se há cadeias vazias ou nulos é uma coisa comum a ser feita, portanto eles fornecem
String.IsNullOrEmpty()
eString.IsNullOrWhiteSpace()
exatamente para esse fim.fonte
NullReferenceException
( msdn.microsoft.com/en-us/library/ms173163.aspx ); você lança umArgumentNullException
se seu método não puder aceitar refs nulos. Além disso, os NullRefs são tipicamente uma das exceções mais difíceis de diagnosticar quando você está corrigindo problemas; portanto, não acho que a recomendação de verificar nulo seja muito boa.ArgumentNullException
tem o benefício adicional de poder fornecer o nome do parâmetro. Durante a depuração, isso economiza ... erro, segundos. Mas segundos importantes.Você pode escrever um método de extensão (pelo que vale a pena):
Agora isso funciona com segurança:
fonte
.EmptyNull()
, por que não simplesmente usar(str ?? "")
onde é necessário? Dito isto, concordo com o sentimento expresso no comentário de @ DaveMarkle: você provavelmente não deveria.null
eString.Empty
são conceitualmente diferentes, e você não pode necessariamente tratar um da mesma forma que o outro.Você também pode usar o seguinte, a partir do C # 6.0
O resultado da sequência será nulo.
fonte
public string Name { get; set; } = string.Empty;
Seqüências de caracteres vazias e nulos são fundamentalmente diferentes. Um nulo é a ausência de um valor e uma sequência vazia é um valor vazio.
A linguagem de programação que faz suposições sobre o "valor" de uma variável, nesse caso, uma sequência vazia, será tão boa quanto iniciar a sequência com qualquer outro valor que não cause um problema de referência nulo.
Além disso, se você passar o identificador para essa variável de cadeia de caracteres para outras partes do aplicativo, esse código não terá como validar se você passou intencionalmente um valor em branco ou se esqueceu de preencher o valor dessa variável.
Outra ocasião em que isso seria um problema é quando a string é um valor de retorno de alguma função. Como string é um tipo de referência e tecnicamente pode ter um valor nulo e vazio, ambos, portanto, a função também pode retornar tecnicamente nulo ou vazio (não há nada para impedir isso). Agora, como existem 2 noções de "ausência de valor", ou seja, uma string vazia e uma nula, todo o código que consome esta função terá que fazer 2 verificações. Um para vazio e outro para nulo.
Em resumo, é sempre bom ter apenas 1 representação para um único estado. Para uma discussão mais ampla sobre vazios e nulos, consulte os links abaixo.
/software/32578/sql-empty-string-vs-null-value
NULL vs Vazio ao lidar com a entrada do usuário
fonte
null
representar a string vazia. Existem várias maneiras pelas quais .net poderia ter implementado semânticas semelhantes, caso elas tivessem optado por fazê-lo, especialmente considerando queString
possui várias características que o tornam um tipo único de qualquer maneira [por exemplo, ele e os dois tipos de matriz são os únicos tipos cuja alocação tamanho não é constante].A razão / problema fundamental é que os projetistas da especificação CLS (que definem como as linguagens interagem com .net) não definiram um meio pelo qual os membros da classe pudessem especificar que deveriam ser chamados diretamente, e não via
callvirt
, sem que o chamador realizasse uma chamada. verificação de referência nula; nem forneceu um meio de definir estruturas que não estariam sujeitas ao boxe "normal".Se a especificação CLS definisse esses meios, seria possível que o .net seguisse consistentemente o lead estabelecido pelo Common Object Model (COM), sob o qual uma referência de cadeia nula fosse considerada semanticamente equivalente a uma cadeia vazia e por outras tipos de classe imutáveis definidos pelo usuário que deveriam ter semântica de valores para definir da mesma forma os valores padrão. Essencialmente, o que aconteceria seria para cada membro
String
, por exemplo,Length
ser escrito como algo parecido[InvokableOnNull()] int String Length { get { if (this==null) return 0; else return _Length;} }
. Essa abordagem ofereceria semântica muito boa para coisas que deveriam se comportar como valores, mas, devido a problemas de implementação, precisam ser armazenados no heap. A maior dificuldade com essa abordagem é que a semântica da conversão entre esses tipos eObject
pode ficar um pouco obscura.Uma abordagem alternativa seria permitir a definição de tipos de estrutura especiais que não foram herdados,
Object
mas que tinham operações personalizadas de boxe e unboxing (que seriam convertidas para / de outro tipo de classe). Sob essa abordagem, haveria um tipo de classeNullableString
que se comporta como a string agora e um tipo de estrutura personalizadaString
, que conteria um único campo particularValue
do tipoString
. A tentativa de converter umString
paraNullableString
ouObject
retornariaValue
se não for nulo ouString.Empty
se for nulo. Tentando converter paraString
, uma referência não nula a umaNullableString
instância armazenaria a referênciaValue
(talvez armazenando nulo se o comprimento fosse zero); lançar qualquer outra referência lançaria uma exceção.Mesmo que as strings precisem ser armazenadas no heap, conceitualmente não há razão para que elas não devam se comportar como tipos de valor que possuem um valor padrão não nulo. Tê-los armazenados como uma estrutura "normal", que continha uma referência, seria eficiente para o código que os utilizava como tipo "string", mas teria adicionado uma camada extra de indireção e ineficiência ao converter para "objeto". Embora eu não preveja o .net adicionando um dos recursos acima até essa data tardia, talvez designers de estruturas futuras possam considerar a possibilidade de incluí-los.
fonte
Nullable<>
, as strings podem ser reimplementadas como tipos de valor; Não posso falar dos custos e benefícios dessa escolha.string
seja mais eficiente do queNullable<string>
seria, ter que usar o método "mais eficiente" é mais oneroso do que poder usar a mesma abordagem para todos os valores de banco de dados anuláveis.Como uma variável de cadeia é uma referência , não uma instância .
Inicializá-lo como Empty por padrão seria possível, mas introduziria muitas inconsistências em todo o quadro.
fonte
string
para ser um tipo de referência. Certamente, os caracteres reais que compõem a string certamente precisam ser armazenados no heap, mas, dada a quantidade de suporte dedicado que as strings já têm no CLR, não seria um exagero terSystem.String
um tipo de valor com um valor único campo privadoValue
do tipoHeapString
. Esse campo seria um tipo de referência e o padrão serianull
, mas umaString
estrutura cujoValue
campo fosse nulo se comportaria como uma sequência vazia. A única desvantagem dessa abordagem seria ... #String
paraObject
, na ausência de código de caso especial no tempo de execução, causaria a criação de umaString
instância em caixa no heap, em vez de simplesmente copiar uma referência aoHeapString
..Length
etc., para que instâncias que contenham uma referência nula não tentaria desreferenciá-la, mas se comportaria conforme apropriado para uma sequência vazia. Se o Framework seria melhor ou pior se fossestring
implementado dessa maneira, se alguém quisessedefault(string)
ser uma string vazia ... #string
ser um invólucro de tipo de valor em um campo de tipo de referência seria a abordagem que exigisse o menor número de alterações em outras partes de .net [na verdade, se alguém estivesse disposto a aceitar a conversão deString
paraObject
criar um item extra em caixa, alguém poderia simplesmenteString
ser uma estrutura comum com um campo do tipoChar[]
que nunca foi exposto]. Eu acho que ter umHeapString
tipo provavelmente seria melhor, mas, de certa forma, a cadeia de valor-tipo que contém aChar[]
seria mais simples.Como cadeias de caracteres são tipos de referência , os tipos de referência são o valor padrão
null
. Variáveis de tipos de referência armazenam referências aos dados reais.Vamos usar a
default
palavra-chave para este caso;str
é umstring
, então é um tipo de referência , então o valor padrão énull
.str
é umint
, então é um tipo de valor , então o valor padrão ézero
.fonte
Errado! Alterar o valor padrão não altera o fato de ser um tipo de referência e alguém ainda pode definir explicitamente a referência como sendo
null
.Verdadeiro ponto. Seria mais sensato não permitir
null
nenhum tipo de referência, mas exigirNullable<TheRefType>
esse recurso.Consistência com outros tipos de referência. Agora, por que permitir
null
tipos de referência? Provavelmente para parecer C, mesmo que essa seja uma decisão de design questionável em um idioma que também forneceNullable
.fonte
Talvez se você usaria
??
operador ao atribuir a variável de corda, que poderia ajudá-lo.fonte
Uma String é um objeto imutável, o que significa que, quando determinado valor, o valor antigo não é apagado da memória, mas permanece no local antigo e o novo valor é colocado em um novo local. Portanto, se o valor padrão de
String a
eraString.Empty
, ele desperdiçaria oString.Empty
bloco na memória quando receber seu primeiro valor.Embora pareça minúsculo, pode se transformar em um problema ao inicializar uma grande variedade de cadeias com valores padrão de
String.Empty
. Claro, você sempre pode usar aStringBuilder
classe mutável se isso for um problema.fonte
String.Empty
. Estou enganado?string
ser uma sequência vazia é permitir todos os bits zero como representação de uma sequência vazia. Há várias maneiras de fazer isso, mas acho que nenhuma envolve a inicialização de referênciasString.Empty
.String.Empty
ou""
.string
locais de armazenamento exigiria também a alteração do comportamento de inicialização de todas as estruturas ou classes que possuem campos do tipostring
. Isso representaria uma mudança muito grande no design do .net, que atualmente espera inicializar zero qualquer tipo sem precisar pensar no que é, exceto apenas pelo tamanho total.Como string é um tipo de referência e o valor padrão para o tipo de referência é nulo.
fonte
Talvez a
string
palavra - chave tenha confundido você, pois se parece exatamente com qualquer outra declaração de tipo de valor , mas na verdade é um alias,System.String
conforme explicado nesta pergunta .Além disso, a cor azul escura no Visual Studio e a primeira letra minúscula podem levar a pensar que é a
struct
.fonte
object
palavra - chave? Embora seja certo que isso é muito menos usado do questring
.int
é um apelido paraSystem.Int32
. Onde você quer chegar? :)System.Int32
é umStruct
, assim, ter um valor padrão, enquantoSystem.String
é umClass
tendo um ponteiro com valor padrãonull
. Eles são apresentados visualmente na mesma fonte / cor. Sem conhecimento, pode-se pensar que eles agem da mesma maneira (= tendo um valor padrão). Minha resposta foi escrito com uma en.wikipedia.org/wiki/Cognitive_psychology ideia psicologia cognitiva por trás dele :-)Tipos anuláveis não chegaram até a versão 2.0.
Se tipos anuláveis tivessem sido criados no início do idioma, a string seria não anulável e a string? teria sido anulável. Mas eles não podiam fazer isso com compatibilidade com versões anteriores.
Muitas pessoas falam sobre ref-type ou não ref type, mas string é uma classe fora do comum e soluções teriam sido encontradas para tornar isso possível.
fonte