domingo, 28 de abril de 2013

Dynamic Method Binding

Já não é novidade que a partir da versão 4 do .NET Framework e da linguagem C# é possível declarar objetos com tipagem dinâmica. Veja este link para mais detalhes.

Em C#, quando um objeto é declarado como dynamico compilador nos permite invocar qualquer método a partir desse objeto, sem checar em tempo de compilação se este método existe ou não. Esse recurso é conhecido como Dynamic Method Binding e apenas em tempo de execução o Framework irá informar, através de uma exceção, que o método invocado não existe.

Exemplo: 
dynamic objetoDesconhecido = ObtemObjetoDesconhecidoDeAlgumLugar();
objetoDesconhecido.MetodoQueNaoExiste(); // Compila mas não executa
Não quero discutir aqui o propósito dessa inclusão à linguagem, quero apenas apresentar uma construção bastante interessante permitida com o uso desse recurso.

Imagine escrever um conjunto de métodos privados sobrecarregados, cada um tratando um tipo específico de entrada e delegar a um único método público a função de decidir qual dos métodos privados deve ser executado.

O exemplo abaixo faz isso:
public class Taxonomy
{
    public string GenusFor(IAnimal animal) {
        if (animal is Lion)
            return GenusFor((Lion)animal);
        if (animal is Zebra)
            return GenusFor((Zebra)animal);
        if (animal is Elephant)
            return GenusFor((Elephant)animal);
        return null;
    }
    private string GenusFor(Lion lion) {
        return "Lion";
    }
    private string GenusFor(Zebra zebra) {
        return "Zebra";
    }
    private string GenusFor(Elephant elephant) {
        return "Elephant";
    }
}
O código acima pode parecer desnecessário se considerarmos que o genero do animal poderia ser uma simples propriedade da interface IAnimal, no entanto serve bem para ilustrar a ideia.

Veja como esse mesmo código ficaria se utilizássemos tipagem dinâmica para deixar que o .NET Framework decida em tempo de execução qual a versão do método GenusFor deve ser executada:
public class Taxonomy
{
    public string GenusFor(IAnimal animal) {
        return ((dynamic)this).GenusFor((dynamic)animal);
    }
    private string GenusFor(Lion lion) {
        return "Lion";
    }
    private string GenusFor(Zebra zebra) {
        return "Zebra";
    }
    private string GenusFor(Elephant elephant) {
        return "Elephant";
    }
}

Usando ((dynamic)this), informamos ao compilador que queremos que o this seja tratado como um tipo dinâmico, assim a existência de um método GenusFor com os parâmetros informados não será verificada em tempo de compilação.

Em tempo de execução, o CLR irá procurar por um método chamado GenusFor no objeto this e encontrará 4 sobrecargas, 1 pública e 3 privadas. Nesses casos, o comportamento do CLR é utilizar o método cujos tipos dos parâmetros esperados sejam o mais próximos possíveis dos parâmetros passados. Isso faz com que o método correto seja invocado, conforme o tipo de IAnimal recebido como parâmetro.

Interessante, não? No entanto lembre-se que recursos poderosos como esse devem ser usados com muita cautela. Brinque um pouco com o código disponível no GitHub e veja o impacto positivo e negativo que esse tipo de abordagem pode trazer.

Confira também o uso desse recurso em um dos exemplos citados no livro Implementing Domain-Driven Design, de Vaugh Vernon.

Abraços.

Nenhum comentário:

Postar um comentário