Usando Exceções Não Tratadas em vez de Contains() em Coleções C#
Ao trabalhar com coleções em C#, os programadores frequentemente precisam determinar se um objeto específico está presente. No entanto, existem casos em que a coleção em questão não possui um método Contains()
embutido, levantando questões sobre como abordar o problema. Uma prática comum, embora questionável, que alguns desenvolvedores adotam é usar exceções não tratadas em vez de verificar explicitamente a existência do objeto. Este post no blog revela por que essa abordagem não é recomendada e explora alternativas melhores.
Entendendo o Problema
Imagine que você está trabalhando com uma coleção de controles em uma aplicação Windows Forms. Você quer verificar um controle específico, mas a coleção não possui um método Contains()
. Duas abordagens possíveis poderiam ser:
- Implementar seu próprio método
Contains()
, que exige laços pela coleção para encontrar o objeto especificado. Isso é geralmente aceito como uma boa prática. - Usar um bloco
try-catch
para tentar acessar o objeto diretamente, onde uma exceção lançada implicaria que o objeto não existe.
try
{
Object aObject = myCollection[myObject];
}
catch(Exception e)
{
// Tratar exceção se o objeto não existir
}
Embora possa parecer conveniente simplesmente capturar uma exceção quando um objeto não é encontrado, isso nos leva a questionar quão ruim essa prática realmente é.
Por Que Usar Exceções para Controle de Fluxo é uma Prática Ruim
1. Sobrecarga de Desempenho das Exceções
Lançar e capturar exceções não é simplesmente um mecanismo para relatórios de erro; isso introduz uma sobrecarga de desempenho. Ao utilizar exceções para controle de fluxo em vez de verificações de laço:
- Percorrer a coleção é direto e acarreta um custo de desempenho conhecido.
- Capturar exceções, no entanto, envolve uma sobrecarga significativa. O tratamento de exceções altera o fluxo do programa, e o uso excessivo leva a um desempenho lento e aplicativos não responsivos.
2. Tratamento de Erros Ofuscado
Uma das questões críticas ao usar exceções para verificar a existência é a perda de especificidade. Quando você captura uma exceção, isso não indica o problema exato. Múltiplas razões podem desencadear uma exceção:
- O objeto específico pode não existir na coleção.
- A coleção pode ser nula ao acesso.
- Problemas podem surgir devido ao casting de tipos.
Todos esses cenários resultam na mesma manipulação genérica de exceções, que não é útil para depuração ou fluxo de controle pretendido.
3. Manutenibilidade do Código
Códigos que utilizam tratamento de exceções para controle de fluxo normal podem ser desafiadores de manter e entender. Desenvolvedores que herdarem esse código terão dificuldades para decifrar a intenção, levando a potenciais bugs e gestão de erros ineficiente.
Alternativas Melhores
Para contornar as armadilhas do uso inadequado de exceções, considere as seguintes abordagens:
- Implemente um Método Contains Personalizado: Esta é a solução mais limpa e eficiente, mantendo todas as operações previsíveis. Um simples laço pela coleção é suficiente.
public bool Contains(Object myObject)
{
foreach (var obj in myCollection)
{
if (obj.Equals(myObject))
{
return true;
}
}
return false;
}
- Use Dicionários ou Hashtables: Se você estiver verificando frequentemente a existência de objetos, usar um
Dictionary
ouHashtable
é uma escolha mais adequada. Essas coleções são otimizadas para consultas por chave e incluem o métodoContainsKey
, tornando a verificação de existência fácil e eficiente.
if (myDictionary.ContainsKey(myObjectKey))
{
// O objeto existe
}
Conclusão
Embora utilizar exceções não tratadas para verificar a existência de objetos em coleções possa parecer uma solução rápida, é uma prática de programação ineficiente e ruim. Um método personalizado Contains()
ou coleções apropriadas não apenas promovem um melhor desempenho, mas também aumentam a clareza, a manutenibilidade e a confiabilidade do seu código.
Para leituras adicionais sobre exceções e seu uso apropriado, consulte estes artigos perspicazes:
Adotar uma abordagem estruturada para o manuseio de coleções garantirá que suas aplicações C# permaneçam eficientes, fáceis de entender e manuteníveis.