C# - Os erros mais comuns cometidos pelos iniciantes - III


Hoje veremos alguns dos erros mais comuns cometidos pelos iniciantes e não iniciantes na linguagem C#.

Continuando a segunda parte do artigo continuo apresentando alguns dos erros mais comuns cometidos por iniciantes na linguagem C#.

Recursos usados:

7 - Usando o tipo de errado de coleção

A linguagem C# fornece uma grande variedade de objetos de coleção. A seguir temos algumas das mais usadas:

Array, ArrayList, BitArray, BitVector32, Dictionary<K,V>, HashTable, HybridDictionary, List<T>, NameValueCollection, OrderedDictionary, Queue, Queue<T>, SortedList, Stack, Stack<T>, StringCollection, StringDictionary

Com tantas opções é quase certo que você vai encontrar a coleção apropriada à tarefa que deseja realizar.

Então dedique algum tempo em pesquisar e escolher o tipo de coleção ideal para a sua finalidade. Isso provavelmente resultará em melhor desempenho e menos espaço para erros.

Se houver um tipo de coleção especificamente direcionado ao tipo de elemento que você vai usar (como string ou bit), prefira usá-lo. A implementação é geralmente mais eficiente quando é segmentada para um tipo específico de elemento.

Para aproveitar o tipo de segurança do C#, geralmente você deve preferir uma interface genérica pois os elementos de uma interface genérica são do tipo que você especifica quando declara seu objeto, enquanto os elementos de interfaces não genéricas são do tipo object.

Ao usar uma interface não genérica, o compilador C# não pode tipar o seu código. Além disso, ao lidar com coleções de tipos de valor primitivos, o uso de uma coleção não genérica resultará em repetidas operações de box/unboxing desses tipos, o que pode resultar em um impacto significativo no desempenho quando comparado a uma coleção genérica do tipo apropriado.

Nota: O processo de conversão explícita de um tipo valor para um tipo referência é conhecido em .NET como Boxing (empacotar). O processo contrário a Boxing é conhecido como Unboxing.

Outro problema comum do C# é escrever seu próprio objeto de coleção. Isso não quer dizer que nunca é apropriado, mas com uma seleção tão abrangente quanto a oferecida pelo .NET, você pode economizar muito tempo usando ou ampliando uma coleção existente, em vez de reinventar a roda.

Assim a plataforma .NET poxssui muitas coleções embutidas para ajudá-lo a armazenar e manipular coleções de dados. Entender como essas coleções funcionam e saber em quais situações cada uma é melhor é uma das principais habilidades necessárias para criar um código com maior desempenho.

Escolher a coleção errada para o trabalho pode tornar seu código muito mais lento ou ainda mais difícil de manter, se você escolher uma que não funcione tão bem ou que não se encaixe exatamente na situação.

Lembre-se de evitar as coleções originais e ficar com as coleções genéricas. Se você precisar de acesso simultâneo, poderá usar as coleções genéricas se os dados forem somente leitura ou considerar as coleções simultâneas para acesso misto se você estiver executando no .NET 4.0 ou superior.

8 - Usando a concatenação de strings ao invés de usar StringBuilder

Lembre-se: "Strings são imutáveis..."

A concatenação de strings funciona de tal maneira que toda vez que você adiciona algo a uma string, um novo endereço na memória é alocado.

A string anterior é copiada para um novo local com a parte recém-adicionada. Isso é uma operação ineficiente.

        static void Main(string[] args)
        {
            List<string> texto = new List<string>() { "Macoratti ", ".net ", " quase tudo ", " para .NET !" };
            string resultado = string.Empty;
            foreach (var valor in texto)
            {
                resultado += valor;
            }
            Console.WriteLine(resultado);
            Console.ReadLine();
        }

Por outro lado, podemos usar o recurso StringBuilder, que mantém a mesma posição na memória sem realizar a operação de cópia.

Graças ao acréscimo das strings por meio do StringBuilder, via método Append, o processo é muito mais eficiente, especialmente no caso de centenas de operações de anexação.

        static void Main(string[] args)
        {
            List<string> texto = new List<string>() { "Macoratti ", ".net ", " quase tudo ", " para .NET !" };

            StringBuilder resultadoStringBuilder = new StringBuilder();

            foreach (var valor in texto)
            {
               resultadoStringBuilder.Append(valor)
            }
            Console.WriteLine(resultadoStringBuilder);
            Console.ReadLine();
        }

A classe System.Text.StringBuilder obtém uma melhor performance no tratamento de strings justamente porque ela aloca o espaço inicial quando uma instância do objeto é criada.  O tamanho padrão inicial é de 16 caracteres na memória quando usamos o construtor da classe sem argumento algum.

9 - Usar First ao invés de FirstOrDefault

Muitos programadores quando procuram ocorrências em um conjunto de elementos usam o método de extensão "Where" e depois retornam a primeira ocorrência usando o método 'First'.

        static void Main(string[] args)
        {
            //INCORRETO
            List<int> numeros = new List<int>() { 1, 4, 5, 9, 11, 15, 20, 21, 25, 34, 55 };
            var resultado = numeros.Where(x => Fibonacci.IsInFibonacciSequencia(x)).First() ;
            Console.WriteLine(resultado);
            Console.ReadLine();
        }

Parece ser bem simples mas você não deve considerar que um valor sempre será encontrado.

Se você usar "First" e nenhum valor for encontrado, uma exceção será lançada. Assim, é melhor usar FirstOrDefault.

Ao usar FirstOrDefault, se nenhum valor for encontrado, o valor padrão para esse tipo será retornado e nenhuma exceção será lançada.

        static void Main(string[] args)
        {
            //CORRETO
            List<int> numeros = new List<int>() { 1, 4, 5, 9, 11, 15, 20, 21, 25, 34, 55 };
            var resultado = numeros.FirstOrDefault(x => Fibonacci.IsInFibonacciSequencia(x));

            Console.WriteLine(resultado);
            Console.ReadLine();
        }

O mesmo raciocíonio vale para Single e SingleOrDefault.

A LINQ possui o método de extensão Single que pode ser usado para retornar somente um elemento em uma sequência que satisfaça uma condição específica.

O problema é que o método Single irá lançar uma exceção do tipo System.InvalidOperationException quando nenhum elemento na sequência satisfazer a condição que foi definida.

Para contornar este problema você pode usar o método de extensão SingleOrDefault que vai retornar o valor padrão do tipo ao invés de lançar a exceção quando nenhum elemento satisfazer a condição.

Nota: Se mais de um elemento na sequência satisfazer a condição ambos os métodos irão lançar a exceção System.InvalidOperationException e neste caso você pode apelar para os métodos de extensão First ou Last.

Na próxima parte do artigo voltarei a falar sobre os erros mais comuns.

"Porque o reino de Deus não consiste em palavras, mas em poder."
1 Coríntios 4:20

Referências:


José Carlos Macoratti