Detectando mudanças em objetos

Há uma porção de funcionalidades dentro do .NET Framework, que podemos incorporar em nossos tipos, para enriqucê-los ainda mais em nível de comportamento. Essas funcionalidades predefinidas, já trazem recursos extremamente interessantes, que com pouco de código extra que utilizamos para “rechear” os pontos customizados, poderemos poupar muitas e muitas linhas de código se fossemos fazer isso manualmente.

Entre as várias funcionalidades que existem e que já falei bastante por aqui, uma delas é a capacidade que temos de dectectar mudanças que acontecem no estado (propriedades) das nossas classes. Geralmente as classes possuem propriedades que podem ser alteradas durante a sua execução, sendo essa alteração realizada através do bloco Set da mesma, ou através de algum método que a manipula internamente.

Por algum motivo, se quisermos detectar que alguma mudança está acontecendo ou já aconteceu, podemos implementar nesta classe as interfaces INotifyPropertyChanging e INotifyPropertyChanged que estão debaixo do namespace System.ComponentModel. Cada uma delas é utilizada para interceptar momentos diferentes, ou seja, a primeira deve ser utilizada quando queremos ser notificados antes da mudança, enquanto a segunda deverá ser utilizada para notificar quando a mudança já aconteceu.

A primeira interface, INotifyPropertyChanging, fornece um único membro, que é o evento PropertyChanging. Já a segunda, INotifyPropertyChanged, disponibiliza um outro evento chamado PropertyChanged. Ambas interfaces podem ser implementadas em classes que você deseja monitorar as alterações que podem acontecer em suas respectivas propriedades. Com isso, podemos permitir aos consumidores desta classe, serem notificados antes e depois da mudança acontecer, podendo assim tomar alguma decisão em cima disso.

Como podemos notar no código abaixo, temos uma classe chamada Cliente, que por sua vez, contém apenas uma única propriedade chamada Nome. Para qualquer evento que você crie, o ideal é criar um método que encapsule a regra para a construção dos parâmetros e o disparo dele, para evitar redundâncias pelo código. Para isso, foram criados dois métodos auxiliares, onde cada um deles encapsula a chamada para o evento que ele gerencia. Note que a atribuição do valor ao membro interno da classe, somente se dá caso o valor que chega para ela seja diferente do qual ela possui, justamente porque não faz sentido notificar alguém que a propriedade foi mudada, mas que efetivamente não foi. É importante notar também que a alteração que será feita na propriedade está envolvida pela chamada dos eventos, ou seja, antes da alteração disparamos o evento PropertyChanging, e depois que a alteração foi realizada, disparamos o evento PropertyChanged.

public class Cliente : INotifyPropertyChanging, INotifyPropertyChanged
{
    private string _nome;

    public string Nome
    {
        get
        {
            return this._nome;
        }
        set
        {
            if (value != this._nome)
            {
                this.OnPropertyChanging(“Nome”);
                this._nome = value;
                this.OnPropertyChanged(“Nome”);
            }
        }
    }

    public event PropertyChangingEventHandler PropertyChanging;

    public event PropertyChangedEventHandler PropertyChanged;

    protected virtual void OnPropertyChanging(string propertyName)
    {
        if (this.PropertyChanging != null)
            this.PropertyChanging(this, new PropertyChangingEventArgs(propertyName));
    }

    protected virtual void OnPropertyChanged(string propertyName)
    {
        if (this.PropertyChanged != null)
            this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
    }
}

Com isso, ao construir um objeto do tipo Cliente, definimos a sua propriedade Nome como “Israel”. Em seguida, nos vinculamos aos eventos PropertyChanging e PropertyChanged, para sermos notificados caso qualquer alteração aconteça nas propriedades deste objeto. Tudo o que estamos fazendo no código abaixo, é escrevendo na tela a notificação da alteração da propriedade Nome. Finalmente, quando alteramos a propriedade Nome de “Israel” para “Israel Aece”, notamos que as mensagens de notificação aparecerão na tela.

Cliente c = new Cliente() { Nome = “Israel” };

c.PropertyChanging += 
    (sender, e) => Console.WriteLine(“Alterando a propriedade ‘{0}’.”, e.PropertyName);

c.PropertyChanged +=
    (sender, e) => Console.WriteLine(“Propriedade ‘{0}’ alterada.”, e.PropertyName);

c.Nome = “Israel Aece”;

Assim como existe essas interfaces que agregam às nossas classes, a possibilidade de serem monitoradas quando alguma mudança acontecer, também há uma nova interface chamada INotifyCollectionChanged (namespace System.Collections.Specialized), que como o nome sugere, permite adicionar à uma coleção, a possibilidade de monitoramento da mesma, onde seremos notificados quando ela for modificada, ou seja, quando elementos forem adicionados ou removidos. Essa interface fornece um único membro, que é o evento CollectionChanged.

Esse evento faz uso de um parâmetro do tipo NotifyCollectionChangedEventArgs, que expõe algumas propriedades interessantes, como por exemplo: Action, NewItems e OldItems. O primeiro deles, retorna uma das opções do enumerador NotifyCollectionChangedAction, dizendo qual foi a ação que aconteceu. Já a propriedade NewsItems, disponiliza um objeto do tipo IList, contendo os novos itens que foram adicionados, enquanto a propriedade OldItens, retorna o mesmo tipo da propriedade anterior, mas com os objetos que foram removidos da coleção.

Essa interface nos permite criar uma coleção que pode ser monitorada quando os elementos dela são manipulados. Felizmente, a Microsoft já adicionou uma coleção genérica chamada ObservableCollection<T>, que está debaixo do namespace System.Collections.ObjectModel, já implementada com essa funcionalidade.

É importante dizer que quando estamos falando no monitoramento de modificações em uma coleção, estamos atentos aos itens que ela armazena, se novos são inseridos, se outros são removidos, alterados, etc. Se uma propriedade de um objeto que está dentro dela for alterada, nada acontecerá à coleção. Como exemplo de uso, podemos visualizar o código abaixo, que cria uma instância desta coleção, definindo o tipo T como sendo Cliente. Antes de começarmos a manipular a coleção, vamos nos vincular ao evento CollectionChanged, para sermos notificados quando novos itens forem adicionados ou removidos.

static void Main(string[] args)
{
    ObservableCollection<Cliente> clientes = new ObservableCollection<Cliente>();
    clientes.CollectionChanged += ColecaoAlterada;

    clientes.Add(new Cliente() { Nome = “Israel” });
    clientes.Add(new Cliente() { Nome = “Claudia” });
    clientes.Add(new Cliente() { Nome = “Virginia” });
}

static void ColecaoAlterada(object sender, NotifyCollectionChangedEventArgs e)
{
    if (e.Action == NotifyCollectionChangedAction.Add)
        VisualizarClientes(“Cliente(s) Adicionado(s)”, e.NewItems);

    if (e.Action == NotifyCollectionChangedAction.Remove)
        VisualizarClientes(“Cliente(s) Removido(s)”, e.OldItems);
}

static void VisualizarClientes(string titulo, IList itens)
{
    if (itens != null && itens.Count > 0)
    {
        Console.WriteLine(titulo);

        foreach (var item in itens)
            Console.WriteLine(“t{0}”, item);
    }
}

Ao executar o código acima, o método ColecaoAlterada será disparado três vezes, sendo uma para cada novo cliente que está sendo adicionado à coleção. Apesar de não estar sendo utilizado aqui, a classe ObservableCollection<T> também implementa a interface INotifyPropertyChanged, que tem a finalidade de monitorar duas propriedades, sendo elas: Items e Count, que são as principais propriedades de qualquer coleção.

Observação: Inicialmente os tipos que vimos aqui para monitoramento de coleções, foram criados para atender ao Windows Presentation Foundation, e justamente por isso, também podem ser encontrados dentro do assembly WindowsBase.dll. A partir da versão 4.0 do .NET Framework, a Microsoft trouxe esses tipos para dentro do assembly System.dll, que nos permite utilizá-los por qualquer tipo de aplicação, sem a necessidade de referenciar diretamente um assembly de uma tecnologia específica.

Conclusão: Vimos no decorrer deste artigo como podemos incorporar em nossas classes a funcionalidade para detectar mudanças, recorrendo à recursos do próprio .NET Framework. Grande parte do que vimos neste artigo, serve como base para grandes funcionalidades que estão espalhados por toda a plataforma .NET, como é o caso do databinding do WPF, consumo de serviços WCF (Data Services), entre outros. A ideia aqui foi apresentar, de uma forma “crua”, essas funcionalidades, para mais tarde abordar outros recursos expostos pelo .NET Framework que fazem uso disso, e que já assumirão o conhecimento que vimos aqui.

Anúncios

Evento de Tecnologia em Hortolândia

No próximo dia 01 de junho (terça-feira) acontecerá um evento na UNASP Hortolândia (Centro Universitário Adventista de São Paulo), na cidade de Hortolândia. Para quem é de Campinas e região, este evento acontecerá na sede da universidade, localizada na Rua Pr. Hugo Gegembauer, 265, Parque Hortolândia, à partir das 19:45. Eu palestrarei sobre o Visual Studio 2010 e o .NET Framework 4.0. É importante dizer que a palestra será de nível 100, que além de abordar superficialmente algumas das principais funcionalidades desta nova versão, falaremos um pouco também sobre algumas tecnologias que já existem e estão à disposição desde as versões anteriores. Abaixo temos a descrição da palestra:

Título: Visual Studio 2010 e .NET Framework 4.0
Palestrante: Israel Aece
Descritivo: Esta palestra tem a finalidade de exibir um “tour” sobre a plataforma .NET, e a principal ferramenta que utilizamos para desenvolver aplicações para ela, que é o Visual Studio. A ideia é demonstrar rapidamente a história e a evolução do .NET Framework até a versão 4.0, incluindo assuntos como as linguagens de programação (C# e VB.NET), acesso à dados e aplicações para internet (WebForms, MVC e Silverlight). Logo após, vamos analisar de forma superficial as novidades que estão sendo incluídas na IDE do Visual Studio 2010 e que dão suporte ao desenvolvimento de qualquer tipo de aplicação sobre a plataforma .NET.

Community Launch em Campinas

No próximo dia 20 de março acontecerá um evento chamado de Community Launch. Esse evento será espalhado por todo o Brasil, onde cada região criará o evento e abordará os principais produtos da Microsoft que estarão sendo lançados neste ano de 2010, a saber: Visual Studio 2010, Windows Server 2008 R2SQL Server 2008 R2.

Para quem é de Campinas e região, este evento acontecerá na Universidade UNIP, localizada no bairro Swift, à partir das 09:00 da manhã. Caso você não saiba exatamente onde ela está localizada, consulte este endereço para saber como chegar até lá. Entre as várias palestras, eu sou o responsável por falar sobre Visual Studio 2010 e .NET Framework 4.0. É importante dizer que a palestra será de nível 100, que além de abordar superficialmente algumas das principais funcionalidades desta nova versão, falaremos um pouco também sobre algumas tecnologias que já existem e estão à disposição desde as versões anteriores. Abaixo temos a descrição da palestra:

Título: Visual Studio 2010 e .NET Framework 4.0
Palestrante: Israel Aece
Descritivo: Esta palestra tem a finalidade de exibir um “tour” sobre a plataforma .NET, e a principal ferramenta que utilizamos para desenvolver aplicações para ela, que é o Visual Studio. A ideia é demonstrar rapidamente a história e a evolução do .NET Framework até a versão 4.0, incluindo assuntos como as linguagens de programação (C# e VB.NET), acesso à dados e aplicações para internet (WebForms, MVC e Silverlight). Logo após, vamos analisar de forma superficial as novidades que estão sendo incluídas na IDE do Visual Studio 2010 e que dão suporte ao desenvolvimento de qualquer tipo de aplicação sobre a plataforma .NET.

IMPORTANTE: Apesar do evento ser gratuito, para poder participar é necessário efetuar o cadastro aqui.

Adicionando Membros em Tempo de Execução

Como sabemos, o C# 4.0 (que virá junto com o Visual Studio .NET 2010) já trará alguns aspectos de linguagens dinâmicas, que permitem a avaliação/checagem de um membro somente em tempo de execução. Para suportar esta funcionalidade, um novo “tipo” foi criado, conhecido como “dynamic”. Ao declarar uma variável do tipo “dynamic”, a checagem de existência de um determinado membro não acontecerá estaticamente, ou seja, independentemente se ele exista ou não, você somente saberá isso em tempo de execução.

Com este tipo especial, você somente conseguirá acessar os membros que, eventualmente, já existam. Mas e como você pode proceder, se quiser construir um tipo dinamicamente? É aqui que entra em cena a classe ExpandoObject (namespace System.Dynamic). Ao instanciar essa classe, você poderá criar tipos dinamicamente, definindo novas propriedades e métodos para este tipo. Para que isso funcione adequadamente, você precisará atribuir a instância desta classe à uma variável do tipo “dynamic”, caso contrário, como vimos acima, a checagem será efetuada durante a compilação, o que não permitirá a compilação. O código abaixo ilustra a utilização desta nova classe:

dynamic obj = new ExpandoObject();
obj.Nome = “Israel”;
obj.Endereco = new ExpandoObject();
obj.Endereco.Cidade = “Valinhos”;
obj.AlgumMetodo = new Action<string>(s => Console.WriteLine(s));

Console.WriteLine(obj.Endereco.Cidade);
obj.AlgumMetodo(“Teste”);

Como podemos notar, instanciamos a classe ExpandoObject para uma variável definida como dinâmica. Depois disso, quando queremos criar “sub-tipos”, basta instanciar uma nova classe ExpandoObject para ele, e seguir criando os novos membros a partir dali. Métodos também podem ser criados, e você pode recorrer a algum delegate já existente para definir a ação a ser executada quando ele for invocado.

Na verdade, esses membros não são incluídos dentro da classe ExpandoObject. A medida que você vai criando estes membros, ele irá armazenando em um dicionário, e quando requisitado, extrai e o executa. Essa funcionalidade é disponibilizada através da Interface IDynamicMetaObjectProvider. Acredito que a performance deva ser mais lenta do que o binding estático, mas é um preço que se deve pagar quando quiser interoperar com linguagens dinâmicas que, além da interoperabilidade com o mundo COM é, ao meu ver, os principais cenários para códigos deste tipo.

Método Zip

Um novo método (de extensão) foi adicionado à classe Enumerable na versão 4.0 do .NET, chamado de Zip. Dado duas coleções/arrays, esse método tem a finalidade de aplicar uma espécie de “zíper” entre eles, ou seja, agrupando os elementos correntes de cada coleção, onde o resultado é a combinação entre esses dois elementos. Para exemplificar, considere os dois arrays de inteiros:

int[] pares = new int[] { 0, 2, 4, 6, 8 };
int[] impares = new int[] { 1, 3, 5, 7, 9 };

foreach
(var item in impares.Zip(pares, (i, p) => string.Format(“{0}, {1}, “, p, i)))
    Console.Write(item);

O resultado ficará: 0, 1, 2, 3, 4, 5, 6, 7, 8, 9. O processamento se encerra quando não for encontrado um elemento “correspondente” na outra coleção.

MultiMethods

É muito comum criarmos métodos em nossas classes que possuam “versões” diferentes. Por “versão”, entenda como overload ou sobrecarga do método. Isso quer dizer que podemos ter um método com o mesmo nome, mas que possuem quantidade ou tipos diferentes.

Em linguagens como o C# e o VB.NET, a resolução de qual dos overloads será invocado é feita em tempo de compilação, baseando no tipo que está sendo passado para o método, o compilador determinará qual das versões invocar. Suponhamos que temos a seguinte estrutura de classes:

public class Pessoa
{
    public string Nome;
}

public class PessoaJuridica : Pessoa
{
    public string CNPJ;
}

public class PessoaFisica : Pessoa
{
    public string CPF;
}

Agora, temos uma classe que tem a finalidade de gerenciar instâncias da classe Pessoa. Essa classe possui dois overloads, onde cada um deles espera uma instância concreta da classe Pessoa:

public class GestorDePessoas
{
    public void AdicionarPessoa(PessoaFisica pf)
    {
        Console.WriteLine("PF");
    }

    public void AdicionarPessoa(PessoaJuridica pj)
    {
        Console.WriteLine("PJ");
    }
}

Nossa aplicação trabalhará de forma independente de qual tipo de Pessoa esteja utilizando. Haverá um método chamado ConstruirPessoa que, dado o nome e o número do documento, retornará uma instância da classe PessoaFisica ou PessoaJuridica que, no nosso caso, deveremos passá-la para a classe GestorDePessoas. Se traduzirmos este parágrafo para código, então teríamos algo como:

Pessoa p = ConstruirPessoa(“Israel Aece”, “123456789”);
GestorDePessoas gp = new GestorDePessoas();
gp.AdicionarPessoa(p);

Se tentarmos compilar, receberemos um erro avisando que não foi possível encontrar um método com essa assinatura. As duas versões do método AdicionarPessoa esperam instâncias das classes concretas (PessoaFisica e PessoaJuridica). Mesmo que “p” armazene internamente a instância de uma classe concreta, ele não conseguirá determinar qual dos métodos invocar, pois somente saberemos o que “p” representa durante a execução da aplicação.

MultiMethods é um conceito que existe para escolher qual dos métodos invocar em tempo de execução, ao contrário do que acontece com o overloading, que fará essa escolha estaticamente, ainda em tempo de compilação. Há várias implementações de MultiMethods para .NET, e uma delas é o MultiMethods.NET. Com essa library, podemos criar apenas uma versão pública do método, que por sua vez, receberá a versão mais básica do tipo, que no nosso caso é a classe Pessoa. Internamente, a library determinará qual dos overloads invocar de acordo com o tipo que está sendo passado. O código abaixo ilustra a classe GestorDePessoas ligeiramente alterada, utilizando a library em questão:

public class GestorDePessoas
{
    private MultiMethod.Action<Pessoa> _adicionar;

    public GestorDePessoas()
    {
        this._adicionar = Dispatcher.Action<Pessoa>(this.AdicionarPessoa);
    }

    public void AdicionarPessoa(Pessoa p)
    {
        this._adicionar(p);
    }

    protected virtual void AdicionarPessoa(PessoaFisica pf)
    {
        Console.WriteLine("PF");
    }

    protected virtual void AdicionarPessoa(PessoaJuridica pj)
    {
        Console.WriteLine("PJ");
    }
}

O código de consumo desta classe que antes não funcionava, passa a funcionar, e em tempo de execução, o método AdicionarPessoa será invocado de acordo com o tipo retornado pelo método ConstruirPessoa. Publicamente temos apenas o método AdicionarPessoa que aceita o tipo base, e dentro dele, utiliza o delegate fornecido pela library para efetuar a escolha do método.

É importante dizer que estas libraries de MultiMethods recorrem a Reflection para efetuar todo o trabalho, e como sabemos, isso possui um overhead extra. Se de um lado ganhamos em flexibilidade, do outro perdemos em performance. Já com o .NET 4.0, o “tipo” dynamic evitará o uso dos MultiMethods. Esta palavra chave informará ao compilador que o método somente deverá ser conhecido em tempo de compilação, também se baseando nos tipos que forem passados à ele.

Ao contrário de grande parte dos tipos do .NET, a keyword dynamic não esta mapeada para algo como System.Dynamic; ao detectar uma variável definida com esta keyword, o compilador efetivamente criará uma variável do tipo System.Object mas, internamente, deverá recorrer à alguma API de DLR para invocar o membro, que talvez possa ser mais performático que Reflection.

Serialização de Propriedades Automáticas

Quando queremos expor uma propriedade, na maioria das vezes existe um campo privado que serve como “repositório” para a informação que é definida ou lida. A propriedade tem o papel de interceptar a leitura e escrita à este campo privado através de blocos get/set.

Para reduzir o trabalho, a partir da versão 3.0 do C#, é disponibilizado uma funcionalidade chamada de Automatic Properties que, por sua vez, nos permite omitir a criação deste campo privado. Na realidade, essa funcionalidade nada mais é do que “açúcar sintático”, já que no momento da compilação, o C# irá injetar o campo privado para armazenar a informação que a propriedade expõe. Como exemplo, imagine que voce cria uma classe com a seguinte definição:

public class Usuario
{
    public string Nome { get; set; }
}

No momento da compilação, ela será definida como:

public class Usuario
{
    [CompilerGenerated]
    private string <Nome>k__BackingField;

    public string Nome
    {
        [CompilerGenerated]
        get
        {
            return this.<Nome>k__BackingField;
        }
        [CompilerGenerated]
        set
        {
            this.<Nome>k__BackingField = value;
        }
    }
}

Já vi alguns casos em que esses objetos que fazem uso desta técnica, são marcados com o atributo SerializableAttribute e, como o próprio atributo indica, permitir a serialização do mesmo. Quando um objeto com essas características são expostos através de serviços WCF (talvez quando voce já possuir uma API com essas classes definidas), precisamos de alguns cuidados especiais.

O serializador padrão do WCF é o DataContractSerializer. Esse serializador trabalha em conjunto com os atributos DataContractAttribute e DataMemberAttribute, serializando as propriedades ou campos públicos que são decorados com um desses dois atributos. Esse serializador também suporta classes decoradas com o atributo SerializableAttribute mas, como esse atributo foi desenhado para persistir e reconstituir na íntegra a representação do objeto, todos os campos (privados ou públicos) são serializados. As propriedades aqui são ignoradas.

Ao submeter um objeto com as características que vimos acima para o serializador padrão do WCF, ele persistirá os campos privados, que no nosso caso é o <Nome>k__BackingField, e que na maioria dos casos, não é isso que desejamos. Como exemplo, podemos utilizar o seguinte código para analisar o resultado deste processo:

new DataContractSerializer(typeof(Usuario))
    .WriteObject(
        File.Create(“Usuario.xml”),
        new Usuario() { Nome = “Israel” });

Abaixo temos conteúdo Xml correspondente:

<Usuario xmlns=”…” xmlns:i=”…”>
  <_x003C_Nome_x003E_k__BackingField>Israel</_x003C_Nome_x003E_k__BackingField>
</Usuario>

Caso voce não possa alterar os atributos que já foram definidos para a classe, uma possibilidade que temos é utilizar o serializador XmlSerializer. Ao contrário do DataContractSerializer, esta classe irá serializar somente as propriedades ou campos públicos. Assim como o exemplo acima, para efetuarmos este teste, podemos utilizar o seguinte código:

new XmlSerializer(typeof(Usuario))
    .Serialize(
        File.Create(“Usuario.xml”),
        new Usuario() { Nome = “Israel” });

E, finalmente, o resultado em Xml:

<?xml version=”1.0″?>
<Usuario xmlns:xsi=”…” xmlns:xsd=”…”>
  <Nome>Israel</Nome>
</Usuario>

O XmlSerializer é utilizado também pelo ASP.NET Web Services (ASMX). Por mais que esta técnica funcione, antes de alterar o serializador padrão do WCF, é importante que voce analise as vantagens e desvantagens da sua utilização e, para isso, poderá recorrer à própria documentação.