Testar se há preenchimento de string – c# .net string

Pergunta:


Ao ler um post sobre boas práticas de programação, mais relacionado à validar o preenchimento de strings, me deparei com o seguinte:

Verificação muito lenta:

string ret = String.Empty;

if (string.IsNullOrEmpty(ret))

Verificação lenta:

string ret = String.Empty;

if (ret == "")

Verificação Performática:

string ret = String.Empty;

if (ret.Length == 0)

Ao ver isso eu fiquei com muita dúvida, pois no local onde trabalho muita gente já havia me falado para utilizar o string.IsNullOrEmpty, mas depois de ver isso estou me questionando.

Então se alguém puder esclarecer o porquê dessa diferença de performance, ou até mesmo se essa informação é real seria interessante.

E caso realmente haja uma forma ideal entre essas 3 formas de verificar o preenchimento de uma string, as outras 2 podem ser “descartadas”, ou a utilização varia de caso para caso?

Autor da pergunta SUR1C4T3

Maniero

Para de ler boas práticas! Isto só cria vícios de programação e ilusão que está aprendendo programar melhor. Estude os fundamentos, entenda porque as coisas funcionam daquele jeito, pesquise e veja por conta próprio ou pergunte para especialistas que podem ser contestados e avaliados por outras pessoas, como está fazendo agora (no passado era mais confiável, hoje o site avalia respostas com problemas como se fossem boas, então também não é tão confiável).

Você fez os testes? Da forma correta? Tem certeza que viu qual é mais lenta? Na situação correta? Tome muito cuidado com testes que não tem controle do ambiente. Vejo muito erro quando a pessoa vai testar e aí dá resultados diferentes da realidade. E mesmo que d~E certo, no uso normal pode ser que o resultado seja diferente do teste real e correto, porque o código não executa isoladamente. Eu fiz o teste da resposta do Rodolfo e na minha máquina, nas condições da minha solução deu resultado diferente, inclusive em execuções diferentes o resultado não foi muito consistente.

Não acontece, mas poderia ter um compilador que analisa todo o contexto (e nem é tão difícil assim em certos casos) e poderia ver que a maior parte dele não é necessária e eliminar tudo.

Ok, intuitivamente até acredito estar certo, mas só um teste correto pode garantir.

O primeiro faz algo diferente dos demais, então já estamos comparando laranjas com bananas. Ele verifica se a string é nula antes de verificar o conteúdo. Se a semântica é diferente já complica a comparação. Aí eu tenho que perguntar: você pode garantir que a string não é nula?

Eu já dei uma resposta sobre o assunto e este método na verdade faz apenas duas coisas: verifica se é nulo e se o seu tamanho é 0. Então já podemos concluir que o próprio .NET prefere verificar o tamanho, e faz sentido, porque evita uma comparação com indireção de memória e compara numericamente uma constante. Lá podemos verificar que o IsNullOrWhiteSpace() é potencialmente muito menos performático e desperdício de recursos se não o que precisa, e a semântica é diferente em certas situações.

Se puder garantir que não é nula então a terceira opção é melhor. Posso afirmar isso sem testar pelo conhecimento que tenho, mas poderia ter alguma otimização e não ser diferente. Nada impede o compilador ou JITter identificar o que quer e trocar por um código mais performático. E isto pode mudar de versão para versão, então se quer uma informação certeira, precisa testar a versão que vai usar, na plataforma que vai usar. Enfim, tudo pode influenciar.

Se quer garantir a melhor performance não conte que haverá otimização. Mas raramente isto é realmente necessário.

E claro, eu evitaria o segundo sempre que possível porque ele tende a não ser otimizado. Eu descartaria o primeiro se garantir que não é nulo, o que eu costumo garantir já e mais ainda no C# 8.

Se alguém achar um motivo para usar outra forma precisa justificar.

Como nota útil em C# 8 será possível garantir que a string e outros tipos por referência nunca seja nulo em tempo de compilação, então qualquer comparação com nulo será desnecessária, a não ser que o tipo seja declarado como anulável (string?).


Nota

Esta parte não faz mais sentido porque a pergunta foi editada, mas pode ser útil para outras pessoas.

Por fim neste caso específico postado na pergunta eu faria assim:

 
 
 

Se eu declarar uma variável do tipo string e não colocar valor algum ela será nula com certeza, não tem que mais nada para fazer, nem precisa verificar se é nula, ainda mais se tem alguma coisa. E claro, os exemplos 2 e 3 darão erro. Então não é que seja boa prática, apenas só faz sentido fazer nada. Respondi considerando que o exemplo foi um erro e a intenção é em outro contexto.

Penso que a questão da performance dos dois primeiros exemplos está relacionada com o facto de estarem a validar o conteúdo da string, onde no 3º exemplo, mais performante, apenas estamos a validar o tamanho.

string.IsNullOrEmpty(ret)

Neste exemplo, o método IsNullOrEmpty representa, internamente, ret == null || ret == string.Empty, onde basicamente estamos a fazer duas comparações para validar o resultado.

Tendo em conta que ret == string.Empty representa string.Equals(ret, String.Empty) onde basicamente compara se o 1º objeto é igual ao 2º, atendendo se um deles é null e mesmo comparando caracter a caracter num loop, compreende-se o porquê de ser a opção mais lenta (até porque temos ainda a comparação com o null).

ret == ""

Neste caso, como já explicado acima, ret == "" representa string.Equals(ret, "") que “arrasta” alguma complexidade na comparação e, por isso, é lenta mas menos que a anterior porque não tem a validação “extra” do ret == null.

ret.Length == 0

Esta é a opção mais rápida se queremos validar se uma string está vazia (sem caracteres) ou não, mas que não é a mais segura, visto que caso a string tenha valor null originará uma exceção de System.NullReferenceException.

Caso tenhamos a certeza absolute que a string não será nunca null, então será uma boa opção.

Conclusão

Cada caso é um caso, e tudo depende do contexto.

A forma mais segura será, com certeza, utilizar IsNullOrEmpty quando não sabemos se o conteúdo será nulo ou não, e mal por mal mais vale prevenir, mas… é preciso avaliar qual das opções, tendo em conta o que temos, será mais viável para uso.

A versão de código “performática” vai dar erro System.NullReferenceException se a variável for null, acredito que terás que alterar para if (ret == null || ret.Length == 0).

Fiz o seguinte teste:

static void Main(string[] args)
    {
        string ret = null;

        Stopwatch stopWatch = new Stopwatch();
        stopWatch.Start();
        for (int i = 0; i <= 100000; i++)
        {
            // if (string.IsNullOrEmpty(ret)) Console.WriteLine($"{i} empty string");
            if (string.IsNullOrEmpty(ret)) Console.WriteLine($"{i} empty string");
        }

        stopWatch.Stop();
        // Get the elapsed time as a TimeSpan value.
        TimeSpan ts = stopWatch.Elapsed;

        // Format and display the TimeSpan value.
        string elapsedTime = String.Format("{0:00}:{1:00}:{2:00}.{3:00}",
            ts.Hours, ts.Minutes, ts.Seconds,
            ts.Milliseconds / 10);
        Console.WriteLine("RunTime " + elapsedTime);

        Console.ReadKey();
    }

Aqui estão os resultados:

if (ret == null || ret.Length == 0)

RunTime 00:00:07.95

if (string.IsNullOrEmpty(ret))

RunTime 00:00:08.43

Eu confesso que nunca me preocupei com esse tipo de otimização, costumo usar string.IsNullOrWhiteSpace() que já verifica também espaços em branco, mas é claro que tem que ver caso a caso o que é melhor usar.

Um jeito de entender o desempenho é testando:

class Program
{
    static void Main(string[] args)
    {
        string test = StrintToTest();

        while(true)
        {
            Console.WriteLine("Press any key. ESC to exit");
            var key = Console.ReadKey();
            if (key.Key == ConsoleKey.Escape) break;

            Console.WriteLine(ElapsedTime("==", () => (test == "")));
            Console.WriteLine(ElapsedTime("Length", () => (test.Length == 0)));
            Console.WriteLine(ElapsedTime("IsNullOrEmpty", () => (string.IsNullOrEmpty(test))));
            Console.WriteLine(ElapsedTime("null or Len", () => (test == null || test.Length == 0)));
            Console.WriteLine();
        }

    }

    public static string ElapsedTime(string what, Func<bool> method)
    {
        var time = new Stopwatch();

        time.Start();
        long i = 0;
        long total = 1000000;
        while (i++ < total)
            method.Invoke();
        time.Stop();

        return string.Format("{0,15} - Elapsed {1,9:f5} ms", what, time.Elapsed.TotalMilliseconds);
    }

    public static string StrintToTest()
    {
        return "The quick brown fox jumps over the lazy dog";
    }
}

Na minha máquina o resultado foi:

             == - Elapsed   7,48910 ms
         Length - Elapsed   6,62160 ms
  IsNullOrEmpty - Elapsed   5,81710 ms
    null or Len - Elapsed   7,22380 ms

Parece justo que checar o tamanho seja mais eficiente do que comparar duas strings.

Chamar o método IsNullOrEmpty da classe string levou menos tempo do que os outros nesse teste. Ao investigar o código fonte do Framework .Net para o método IsNullOrEmpty temos o seguinte:

public static bool IsNullOrEmpty(String value) {
    return (value == null || value.Length == 0);
}

Então parece haver alguma otimização do compilador quando você utiliza o método da classe string. Será?

A questão do desempenho, você precisa fazer os testes para escolher o que for melhor, porém entenda que desempenho é muito relativo. A execução de um código deve considerar muitos fatores do ambiente de execução que você não controla e a maior parte das aplicações não tem a necessidade de otimização de desempenho nesse nível de instruções.

O que sobra então são questões que tem mais a ver com a organização do programa/código. O “código limpo”, “código legível”, “código organizado” ou o código que segue algum tipo de padrão ou estilo. Isso é o que a gente acaba chamando de código escrito com boas práticas.

Entrando na opinião pessoal, eu lembro que no passado, sempre que a gente entrava num time de desenvolvimento procurava “o guia de boas práticas”. Na verdade esse guia era mais direcionado para que o código ficasse de uma certa forma padronizado porque as pessoas tem várias experiências diferentes em vários lugares e acabava que cada um escrevia o código de forma diferente. Não era uma questão do código ser bom ou ruim, rápido ou lento, era uma questão do código ser entendido mais rapidamente por qualquer pessoa da equipe. Se o seu time pode ter esse privilégio, eu não vejo como isso possa atrapalhar ou ser ruim, então antes de seguir as boas práticas, talvez seja necessário entender o que significam as boas práticas naquela comunidade/time que você está inserido.

Se você quiser avaliar se uma string possui valor, o C# possui uma função do próprio tipo primitivo string.

Ex:

string.IsNullOrEmpty("abc");

Se você quiser avaliar se uma string possui valor, abstraindo espaços em branco, o C# possui uma função do próprio tipo primitivo string.

Ex:

string.IsNullOrWhiteSpace("abc");

Independente da linguagem, você pode avaliar:

var mstr= ''; if(mstr == null) return true;

E o resultando será false.

Fonte

Related Posts:

Qual a diferença entre AppCompatActivity e Activity? – android android-activity
Pergunta: Qual a diferença da AppCompatActivity para Activity ? A partir de qual versão a AppCompatActivity foi adicionada ao Android? Autor da pergunta Luhhh A diferença reside ...
Como abreviar palavras em PHP? – php string
Pergunta: Possuo informações comuns como nome de pessoas e endereços, e preciso que elas contenham no máximo 30 caracteres sem cortar palavras. Exemplo: 'Avenida Natalino João Brescansin' ...
Qual é a finalidade de um parêntese vazio numa declaração Lambda? – c# expressões-lambda característica-linguagem
Pergunta: Criei um exemplo de uma declaração Lambda sem argumentos, entretanto, estou com duvidas referente a omissão do parêntese vazio () na declaração. Veja o exemplo: class ...
Boas práticas para URI em API RESTful – api rest restful
Pergunta: Estou com dúvida em relação às URIs de alguns recursos da api que estou desenvolvendo. Tenho os recursos projetos e atividades com relação 1-N, ...
Dúvidas sobre a integração do MySQL com Java – java mysql netbeans
Pergunta: Estou criando um sistema no NetBeans, utilizando a linguagem Java e o banco de dados MySQL. Escrevi o seguinte código para realizar a conexão ...
Qual é a finalidade da pasta Model do framework Inphinit? – php inphinit
Pergunta: No Inphinit micro-framework existe a pasta Model que fica dentro da pasta application, e nela é onde ficam as classes, mas eu estou muito ...
Uso do ‘@’ em variáveis – javascript typescript coffeescript
Pergunta: Vejo em algumas linguagens que compilam para javascript, como TypeScript e CoffeeScript, o uso do @ em variáveis, como também, casos em que o ...
Qual tamanho máximo um arquivo JSON pode ter? – json arquivo
Pergunta: Vou dar um exemplo para conseguir explicar minha duvida: Preciso recuperar informação de imagens vindas de uma API, esse banco de imagens me retorna JSON's ...
O que é Teste de Regressão? – terminologia engenharia-de-software testes
Pergunta: Na matéria de Teste de Software o professor abordou um termo chamado Teste de Regressão, isto dentro da disciplina de teste de software. Sendo ...
O que é um construtor da linguagem? – php característica-linguagem
Pergunta: Em PHP, já li e ouvi várias vezes a respeito dos Construtores da Linguagem. Os casos que sempre ouvi falar deles foi em casos ...
Função intrínseca para converter numérico para string – cobol
Pergunta: Estou a tentar saber se existe alguma função intrínseca do COBOL para converter um data numérico para string sem precisar usar a cláusula REDEFINES: ( ...
Porque usar implements? – java android
Pergunta: Qual a diferença entre usar btn.setOnClickListener(new OnClickListener() { e public class MainActivity extends Activity implements OnClickListener{ Estive fazendo um curso de Android e meu professor falou que ...
O que é XHTML e quando deve ser usado? – html xml xhtml
Pergunta: O que eu sei é que o XHTML precisa ser XML válido. Isso implica, por exemplo, que todas as tags precisam ser fechadas. Por ...
Uma placa aceleradora de vídeo pode melhorar o desempenho não-gráfico? [fechada] – desempenho
Pergunta: Para desenvolver em Ruby on Rails, eu utilizo aqui uma máquina virtual do VirtualBox com Ubuntu Server 14.04 sem interface gráfica instalada. Recentemente descobri uma ...
Concat() VS Union() – c# .net
Pergunta: Qual a diferença entre Concat() e Union() ? Quando usar Concat() e quando usar Union() ? Somente pode ser usado em list ? ...

Deixe uma resposta

O seu endereço de email não será publicado. Campos obrigatórios marcados com *