Introdução
Quando escrevemos código reaproveitável, criamos funções e/ou classes que recebem parâmetros. Na maioria das vezes esses parâmetros são fundamentais na implementação da lógica dentro de uma classe ou método, e o programador mais experiente irá, de forma defensiva, verificar se um determinado parâmetro recebido é válido antes de prosseguir com a lógica, e caso contrário, informar ao cliente (que está consumido o código) do erro cometido com a exceção correta.Exemplo fictício
Vamos supor que temos uma classe chamada Formulario. Essa classe, quando instanciada, recebe em seu construtor uma instância de outra classe chamada Registro, que representa um registro no banco de dados, veja o código abaixo:
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/// <summary> | |
/// Representa um registro de um banco de dados | |
/// </summary> | |
public class Registro { | |
public void Salvar() { | |
} | |
} | |
/// <summary> | |
/// Representa um formulário que altera um registro | |
/// </summary> | |
public class Formulario { | |
readonly Registro _registro; | |
public Formulario(Registro registro) { | |
_registro = registro; | |
} | |
/// <summary> | |
/// Grava o registro quando o usuário clica em um botão | |
/// </summary> | |
public void GravarClick() { | |
_registro.Salvar(); | |
} | |
} |
Note que o formulário recebe um registro, e quando o usuário clicar no botão Gravar, o registro por si mesmo irá ser persistido no banco de dados. Para consumir esse código, basta fazer a seguinte chamada:
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
var registro = new Registro(); | |
var formulario = new Formulario(registro); |
Até aí tudo bem, mas a linguagem permite que façamos o seguinte código, sem erros de compilação:
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
var formulario = new Formulario(null); |
O erro só será visto em tempo de execução, pois quando o usuário clicar em Gravar, o valor de "registro" é nulo e consequentemente o .NET irá lançar uma exceção não tratada e a aplicação provavelmente irá parar de funcionar.
Adicionando ArgumentNullException
É uma boa prática verificar se um parâmetro é nulo antes de sair consumindo o valor passado por referência.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
public class Formulario { | |
readonly Registro _registro; | |
public Formulario(Registro registro) { | |
if (registro == null) | |
throw new ArgumentNullException("registro"); | |
_registro = registro; | |
} | |
} |
Desta maneira, destacamos a importância do valor do parâmetro na classe, e que sem um valor válido, lançaremos uma exception informando o nome do argumento inválido. Vale ressaltar que classe ArgumentNullException recebe uma string que representa o nome do argumento que estamos tratando e esse valor deve ser passado de forma correta. Assim, se o cliente que estiver consumindo esse código tratar a excessão dentro de um bloco Try/Catch, ele terá essa informação de forma precisa.
Evitando código tedioso
Imagine agora um método e que contenha 4 parâmetros, todos eles obrigatórios. Teríamos o trabalho tedioso de escrever 4 vezes a mesma expressão, além do trabalho de digitar na mão o nome dos argumentos ao lançar a exception.
Para automatizar essa tarefa, criei uma classe chamada Requires, contendo um método chamado That, para utilizar da seguinte maneira:
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
public class Formulario { | |
readonly Registro _registro; | |
public Formulario(Registro registro) { | |
Requires.That(() => registro != null); | |
_registro = registro; | |
} | |
// ... | |
} |
O método That recebe uma Lambda Expression que representa uma função que retorna verdadeiro ou falso. Desejamos que o parâmetro "registro" não seja nulo. Caso o valor seja nulo, o método lança automaticamente para nós um ArgumentNullException com o nome do parâmetro corretamente, sem nos preocuparmos em digitar isso na mão, facilitando a nossa vida.
A classe requires ficou conforme o código abaixo:
A classe requires ficou conforme o código abaixo:
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using System.Linq.Expressions; | |
namespace MyNamespace { | |
public static class Requires { | |
/// <summary> | |
/// Compiles the given boolean expression and executes the resulting delegate. | |
/// If returns False, throws an ArgumentNullException with the correct parameter name | |
/// </summary> | |
/// <param name="verifiableExpresson"></param> | |
public static void That(Expression<Func<bool>> verifiableExpresson) { | |
var binaryExpression = (BinaryExpression)verifiableExpresson.Body; | |
var memberExpression = (MemberExpression)binaryExpression.Left; | |
var paramName = memberExpression.Member.Name; | |
var compiledExpression = verifiableExpresson.Compile(); | |
if (!compiledExpression.Invoke()) | |
throw new ArgumentNullException(paramName); | |
} | |
} | |
} |
Outro exemplo com Extension Method
Extension Methods recebem pelo menos um parâmetro (this), e devemos verificar se possuem valores. É muito fácil passar valores nulos para ExtensionMethods, basta fazer um Cast implícito.
No exemplo abaixo, possuímos uma classe Cliente que implementa a interface IEntidade.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/// <summary> | |
/// Representa o Estado atual da entidade | |
/// </summary> | |
public enum EEstado { | |
Editando, | |
Inserindo | |
} | |
/// <summary> | |
/// Interface comum para entidades que possuem Estado | |
/// </summary> | |
public interface IEntidade { | |
EEstado Estado { get; set; } | |
} | |
public class Cliente : IEntidade { | |
public string Nome { get; set; } | |
public EEstado Estado { get; set; } | |
} | |
public static class Extensoes { | |
public static void AlteraEstado(this IEntidade entidade, EEstado estado) { | |
entidade.Estado = estado; | |
} | |
} |
Como no caso anterior, podemos facilmente escrever um programa que consome essas classes chamando o extension method criado:
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
public static class Program { | |
public static void Main() { | |
var cliente = new Cliente(); | |
cliente.AlteraEstado(EEstado.Inserindo); //utiliza o extension method | |
} | |
} |
No ExtensionMethod "AlteraEstado", não verificamos se passamos um valor nulo (entidade). Se escrevermos o seguinte código, a aplicação compilará normalmente, e só veremos o problema em tempo de execução, pois o cast que foi feito retornará nulo:
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
public static class Program { | |
public static void Main() { | |
var cliente = new object(); | |
//consome o extension method, passando um valor nulo (object não implementa IEntidade) | |
(cliente as IEntidade).AlteraEstado(EEstado.Inserindo); | |
} | |
} |
O Extension Method AlteraEstado deve ser modificado para ficar assim:
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
public static class Extensoes { | |
public static void AlteraEstado(this IEntidade entidade, EEstado estado) { | |
Requires.That(() => entidade != null); | |
entidade.Estado = estado; | |
} | |
} |
Dessa forma o programador será informado corretamente sobre um erro de argumento nulo, e terá oportunidade de fazer correções antes de entregar ao cliente. =)
Nenhum comentário:
Postar um comentário