Ouvi dizer que é recomendável validar argumentos de métodos públicos:
- Deve-se procurar nulo se ele não espera nulo?
- Um método deve validar seus parâmetros?
- MSDN - CA1062: Validar argumentos de métodos públicos (eu tenho o .NET background, mas a pergunta não é específica do C #)
Motivação é compreensível. Se um módulo for usado de maneira errada, queremos lançar uma exceção imediatamente, em vez de qualquer comportamento imprevisível.
O que me incomoda é que argumentos errados não são o único erro que pode ser cometido ao usar um módulo. Aqui estão alguns cenários de erro em que precisamos adicionar a lógica de verificação se seguirmos as recomendações e não quisermos a escalação de erros:
- Chamada recebida - argumentos inesperados
- Chamada recebida - o módulo está em um estado incorreto
- Chamada externa - resultados inesperados retornados
- Chamada externa - efeitos colaterais inesperados (entrada dupla em um módulo de chamada, quebrando outros estados de dependências)
Eu tentei levar em conta todas essas condições e escrever um módulo simples com um método (desculpe, não caras do C #):
public sealed class Room
{
private readonly IDoorFactory _doorFactory;
private bool _entered;
private IDoor _door;
public Room(IDoorFactory doorFactory)
{
if (doorFactory == null)
throw new ArgumentNullException("doorFactory");
_doorFactory = doorFactory;
}
public void Open()
{
if (_door != null)
throw new InvalidOperationException("Room is already opened");
if (_entered)
throw new InvalidOperationException("Double entry is not allowed");
_entered = true;
_door = _doorFactory.Create();
if (_door == null)
throw new IncompatibleDependencyException("doorFactory");
_door.Open();
_entered = false;
}
}
Agora é seguro =)
É bem assustador. Mas imagine como pode ser assustador em um módulo real com dezenas de métodos, estado complexo e muitas chamadas externas (oi, amantes de injeção de dependência!). Observe que se você está chamando um módulo cujo comportamento pode ser substituído (classe não selada em C #), está fazendo uma chamada externa e as consequências não são previsíveis no escopo do chamador.
Resumindo, qual é o caminho certo e por quê? Se você pode escolher entre as opções abaixo, responda a perguntas adicionais, por favor.
Verifique o uso de todo o módulo. Precisamos de testes de unidade? Existem exemplos desse código? A injeção de dependência deve ter uso limitado (uma vez que causará mais lógica de verificação)? Não é prático mover essas verificações para o tempo de depuração (não inclua na liberação)?
Verifique apenas argumentos. Pela minha experiência, a verificação de argumentos - especialmente a verificação nula - é a verificação menos eficaz, porque os erros de argumento raramente levam a erros complexos e a escalamentos de erros. Na maioria das vezes, você receberá um NullReferenceException
na próxima linha. Então, por que as verificações de argumentos são tão especiais?
Não verifique o uso do módulo. É uma opinião bastante impopular, você pode explicar por quê?