Desenvolvimento - C#
Strings em C# - Coisas que nem todos sabem
Este artigo ilustra os erros comuns na utilização de Strings em C#, além de dicas sobre como melhorar a performance de aplicações que manipulam dados em forma de texto.
por Marcio Paulo Mello MartinsDescrição
Este artigo ilustra os erros comuns na utilização de Strings em C#, além de dicas sobre como melhorar a performance de aplicações que manipulam dados em forma de texto.
Introdução
Todo mundo conhece strings (para ser mais exato, uma instância da classe System.String), certo? Sim ... sequências de caracteres são comuns quando se manipula texto. Aliás, todo texto é uma string. Então, todo mundo está careca de saber como se trabalha com strings, certo?
ERRADO!
Já vi muita gente usando strings de forma errada, e isso é muito mais comum do que se pensa.
Vamos enumerar aqui alguns casos e dicas que merecem ser vistos pela maioria dos desenvolvedores.
1 - Obter a referencia uma String através de um Object
Um dos erros mais gritantes que se pode cometer utilizando strings é quando se obtém a referência de uma string cuja instância é um Object. Por exemplo, imagine que você tem um DataTable contendo uma DataColumn cujo tipo é String; aí você quer pegar o valor de um elemento que está em uma DataRow, e faz o seguinte:
Listagem 1:Pegando elemento de um DataRow
string str = dataTable.Rows[0][1].ToString();
O objeto retornado pela chamada de dataTable.Rows[0][1] é do tipo Object mas, no nosso caso, já contém um valor do tipo String, o que desobriga o desenvolvedor a chamar o método ToString() para esta instância. Toda vez que chamamos o método ToString(), ele cria uma nova string que conterá o valor a ser retornado. Ou seja, teremos duas instâncias de String contendo exatamente o mesmo conteúdo na memória, o que é redundante e causa menor performance. O correto é fazer um cast direto, desta forma:
Listagem 2: Criando um cast direto
string str = (string)dataTable.Rows[0][1];
Desta forma, o compilador entende que o valor passado já é do tipo String, e passa a referência da memória diretamente para o objeto, sem necessidade de processamento ou criação de novos objetos internamente.
Agora, se seu dado pode ser nulo, a coisa muda de figura.
Caso o dado que você está passando para uma string possa ser um DbNull (nulo no banco de dados), aí sim eu RECOMENDO e ENFATIZO o uso do método ToString() como demonstrado no primeiro exemplo, pois este método converte um dado nulo para uma string vazia, evitando problemas de conversão do tipo DbNull.Value para String.
Portanto, CUIDADO !!
2 - Concatenação de String
Isso é uma novela mexicana ... todo mundo fala, todo mundo recomenda, mas poucos fazem da forma correta.
Quando se modifica o conteúdo de uma String, não se modifica o conteúdo de uma String! Uma nova String é criada na memória contendo a concatenação das duas Strings, e a nova referência é passada para a variável. Portanto, ao concatenarmos strings, criamos instâncias órfãs na memória.
Aí você me diz: Ah, Marcio ... você é um Mané! O Garbage Collector recolhe esse lixo da memória! Sim, claro que recolhe. Então, isso não causa impacto se seu escopo for aplicações Desktop. Mas se estivermos falando de aplicações Web, Windows Services ou de WCF Services, com milhares de solicitações por segundo? Isso poderia gerar quantidades absurdas de strings perdidas na memória!
Então, ao invés de fazer isto:
Listagem 3: Modificando o conteúdo de uma String
string str = "Marcio"; str += " Paulo";
Faça isto:
Listagem 4: Modificando o conteúdo de uma String corretamente
StringBuilder sb = new StringBuilder(); sb.Append("Marcio"); sb.Append(" Paulo"); string str = sb.ToString();
Usando a classe StringBuilder você consegue efetuar milhares de concatenações por segundo, de forma rápida e otimizada, sem gerar resíduos na memória. Poderíamos até chamar de uma Concatenação Ecológica!
3 - Comparação de Strings
Quase todo mundo tem dúvidas nesta questão. Qual é a melhor forma para comparar se duas strings são iguais? Alguns usam o comparador de igualdade (==), ou o método Equals, desta forma:
Listagem 5: Comparando strings
bool resultado = a ==b;
ou
Listagem 6: Outra forma de comparar Strings
bool resultado = a.Equals(b);
Claro ... dá certo. Mas e se um dos objetos for nulo? Aí lascou ...
Mas em verdade, em verdade vos digo: existe um meio mais eficiente de comparar strings, inclusive verificando se o objeto é nulo, levando-se em conta (ou não) o casing (maiúsculas e minúsculas)! Basta utilizar o método String.Compare(string a, string b, bool ignoreCase).
Veja:
Listagem 7: Usando String.Compare
bool resultado = string.Compare(a, b, true) == 0;
Na verdade, este método retorna o seguinte:
- -1, se a maior que b;
- 0 , se a igual a b;
- 1, se a menor que b;
Então já sabem: String.Compare: Comparou, usou!
4 - Verificação de Strings vazias
Meu amigo... pelo amor de Deus! Não faça como o infeliz que escreveu a instrução abaixo:
Listagem 8: Verificando Strings Vazias
if (str == null || str =="") Console.WriteLine("String nula ou vazia");
Se você precisa testar se uma string é nula ou vazia, use o método String.IsNullOrEmpty(string a). Veja:
Listagem 9: Verificando Strings vazias da maneira certa
if (string.IsNullOrEmpty(str)) Console.WriteLine("String nula ou vazia");
Este método é muito mais eficiente, já que é otimizado para verificar rapidamente se uma String é nula ou vazia.
5 - String.Empty versus "" ... ESQUEÇA !!
Há um debate em todas as galáxias do universo conhecido sobre este tema: qual é a melhor notação para descrever uma string vazia - String.Empty ou ""?
Na verdade, não existe uma diferença significante entre uma ou outra opção. Testes mostram que a diferença de performance entre uma e outra forma é desprezível, mesmo quando são criadas bilhões de strings vazias.
6 - Criação de strings com elementos repetidos
Já vi formas mirabolantes de preencher strings com caracteres repetidos, mas queria apenas mostrar a que considero mais simples:
Listagem 10: Preenchendo Strings
string s = new string('*', 20);
Desta forma, estamos criando uma string contendo vinte caracteres '*' (asterisco). Simples, não?
7 - IndexOf com case insensitive
A classe String possui várias implementações do método IndexOf, que localiza em uma string a posição de outra string dada. O problema é que todas as implementações são "case sensitive", ou seja, levam em consideração caracteres maiúsculos e minúsculos.
O que pouca gente sabe é que, para resolver este problema, existe a classe CompareInfo dentro do namespace Globalization. Esta classe possui um método IndexOf que não leva em conta maiúsculas e minúsculas (case insensitive). Veja:
Listagem 11: Usando IndexOf
string s1 = "Trabalhar com C# é DEMAIS!": string s2 = "demais"; System.Globalization.CompareInfo compare = CultureInfo.InvariantCulture.COmpareInfo; int i = compare.IndexOf(s1,s2, CompareOptions.IgnoreCase);
Desta forma, teremos o índice do primeiro caractere da expressão “DEMAIS”, mas usando como padrão sua forma minúscula “demais”.
8 – Conversão de string para números
Este é um ponto que também é alvo de incompreensão e equívocos. Como converter uma string para um número? Muito simples.
Cada tipo numérico possui um método estático chamado TryParse. Para converter uma string num tipo numérico específico, basta criar uma variável do tipo desejado e passá-la como parâmetro de saída do método TryParse. Assim:
Listagem 12: Usando TryParse
string s = "42"; int i; if(int.TryParse(s, out i)) { Console.WriteLine("Conversão OK!"); } else { Console.WriteLine("Conversão falhou!"); }
Conclusão
Existem zilhões de maneiras de se utilizar strings, mas nem todas são as mais indicadas para determinados casos.
O importante é que você analise seu caso e entenda as implicações da utilização de certos tratamentos para strings.
Usando as técnicas recomendadas acima, você estará de acordo com as melhores práticas utilizadas no mercado de desenvolvimento nos dias de hoje.
Até a próxima!