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 Martins



Descriçã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 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!

Marcio Paulo Mello Martins

Marcio Paulo Mello Martins - Bacharel em Ciência da Computação pela FASP; MCP, MCAD, MCSD, MCTS, MCPD e MCT. Atua há mais de 10 anos com desenvolvimento de software e treinamento em tecnologias Microsoft, trabalhando hoje como Analista Desenvolvedor na F|Camara (http://www.fcamara.com.br), além de ser proprietário da Logical Docs (http://www.logicaldocs.com.br), empresa do ramo de gerenciamento eletrônico de documentos. Quando sobra um tempinho, é pianista e toca em uma banda de Jazz.