Mostrando postagens com marcador Generics. Mostrar todas as postagens
Mostrando postagens com marcador Generics. Mostrar todas as postagens

Método de Extensão para encontrar controles em Windows Forms

Recentemente em uma thread nos fóruns do MSDN respondi uma pergunta que me levou a compartilhar aqui a solução. A dúvida era criar um método para localizar os controles do tipo PictureBox existentes em um formulário.

A solução, que é relativamente simples, seria criar um método recursivo para fazer um loop na coleção de controles do formulário e ir testando esses controles para verificar se cada controle é do tipo PictureBox. Como existem controles que são containers (contém outros controles), o método seria chamado novamente, dai a recursividade.

Vamos estender a funcionalidade acima para qualquer tipo de controle fazendo com o que o nosso tipo seja o parâmetro para o método, logo podemos criar um método genérico no formulário. Veja o método logo abaixo.

public partial class SeuForm : Form
{
    public SeuForm()
    {
        InitializeComponent();
    }

    private List<T> GetChildControls<T>(Control c, bool recursive)
        where T : Control
    {
        // lista de controles
        var childControlsList = new List<T>();

        // adiciona controles na lista de controles
        childControlsList.AddRange(c.Controls.OfType<T>());

        // verifica se o método é recursivo e realiza
        // um loop nos filhos
        if (recursive)
        {
            foreach (Control ctrl in c.Controls)
                childControlsList.AddRange(this.GetChildControls<T>(ctrl, true));
        }

        return childControlsList;
    }
}

Vejam que anteriormente escrevi: “…fazer um loop na coleção de controles do formulário e ir testando esses controles para verificar se cada controle é do tipo PictureBox.”. Para isso, o método acima utiliza o método de extensão OfType.

O parâmetro recursive tem por objetivo informar o método que queremos torná-lo recursivo, ou seja, se verdadeiro queremos que o método liste os controles mesmo que estes estejam contidos em outros controles containers.

Ok, isso resolve o problema. Mas podemos melhorar essa solução?

Sim, podemos reutilizar o método em outros projetos criando um método de extensão para a classe Control, que é a classe base para todos os controles Windows Forms.

Temos então a classe abaixo que pode ser criada em uma Class Library e então ser reutilizada em outros projetos.

public static class ControlExtensions
{
    public static List<T> GetChildControls<T>(this Control c, bool recursive)
        where T : Control
    {
        // lista de controles
        var childControlsList = new List<T>();

        // adiciona controles na lista de controles
        childControlsList.AddRange(c.Controls.OfType<T>());

        // verifica se o método é recursivo e realiza
        // um loop nos filhos
        if (recursive)
        {
            foreach (Control ctrl in c.Controls)
                childControlsList.AddRange(ctrl.GetChildControls<T>(true));
        }

        return childControlsList;
    }
}

É algo simples, mas que achei que seria interessante compartilhar essa solução.

Para finalizar, vale a pena conferir essa solução do Israel Aece e essa biblioteca do Márcio Althmann. Não são relacionadas com Windows Forms mas utilizam métodos de extensão para outras finalidades.

Links de ajuda:

Control class
http://bit.ly/PbbG9

Control.ControlCollection class
http://bit.ly/dAV7rI

Enumerable.OfType<TResult> Method
http://bit.ly/boCA6y

C# Tips & Tricks - Métodos ForEach

É comum criarmos loops for each em nosso código para realizarmos uma determinada ação nos items de uma lista ou array. Alguns desenvolvedores não sabem que desde a versão 2.0 do .NET Framework existem métodos que simplificam bastante essa abordagem.

Vamos criar uma classe simples para utilizarmos posteriormente no código.

public class Customer
{
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public string EmailAddress { get; set; }

    public override string ToString()
    {
        return string.Format("{0} {1}\nEmail: {2}", FirstName, LastName, EmailAddress);
    }
}

Essa classe representa um cliente que possui um nome, sobrenome e e-mail. Além disso, sobrescreve o método ToString de System.Object e utiliza propriedades automáticas (C# 3.0).

Vamos agora criar uma lista genérica de clientes.

var lista = new List<Customer>
{
    new Customer { FirstName = "Ari", LastName = "Raimundo", EmailAddress = "ari@mail.com"},
    new Customer { FirstName = "Anders", LastName = "Hejlsberg", EmailAddress = "anders@mail.com"},
    new Customer { FirstName = "Bill", LastName = "Gates", EmailAddress = "bill@mail.com"},
    new Customer { FirstName = "Jesse", LastName = "Liberty", EmailAddress = "jesse@mail.com"}
};

Digamos que queremos alterar o sobrenome de todos os clientes para letras maiúsculas. O código abaixo seria o caminho mais comum.

foreach (var c in lista)
    c.LastName = c.LastName.ToUpper();

O que poucos conhecem é que a classe List possui o seguinte método.

public void ForEach(Action<T> action);

O método ForEach realiza a ação especificada pelo parâmetro action em todos os itens da lista. O parâmetro action é do tipo System.Action<T>, ou seja, encapsula um método que não retorna valor e possui um parâmetro que é o item da lista.

Podemos substituir o loop então da seguinte forma:

lista.ForEach(c => { c.LastName = c.LastName.ToUpper(); });

Estou usando uma expressão lambda para representar o parâmetro action. O range dessa expressão seria a variável c, que é do tipo Customer.

Vamos modificar o código um pouco para mostrar os clientes na tela de console.

lista.ForEach(c =>
{
    c.LastName = c.LastName.ToUpper();
    Console.WriteLine(c);
});

Simples não é? Agora vamos fazer o mesmo com o array do código abaixo.

var array = new Customer[]
{
    new Customer { FirstName = "Ari", LastName = "Raimundo", EmailAddress = "ari@mail.com"},
    new Customer { FirstName = "Anders", LastName = "Hejlsberg", EmailAddress = "anders@mail.com"},
    new Customer { FirstName = "Bill", LastName = "Gates", EmailAddress = "bill@mail.com"},
    new Customer { FirstName = "Jesse", LastName = "Liberty", EmailAddress = "jesse@mail.com"}
};

A diferença agora é que iremos utilizar um método estático da classe System.Array. Esse método tem dois parâmetros: o array e a ação a ser executada em seus itens.

public static void ForEach<T>(T[] array, Action<T> action);

Para fazer o loop e mostrarmos os clientes na tela de console utilizamos o código abaixo.

Array.ForEach(array, c =>
{
    c.LastName = c.LastName.ToUpper();
    Console.WriteLine(c);
});

Bom, você deve estar se perguntando: qual a diferença? Existe diferença de performance? Bom, não fiz nenhum teste, vejam o link abaixo e tirem suas próprias conclusões.

Performance of foreach vs. List.ForEach
http://bit.ly/5VKDrD

Particularmente, sugiro que utilizem esses métodos.

Um método pode retornar um tipo anônimo?

Os tipos anônimos (Anonymous Types) são uma característica da linguagem C# onde criamos diversas propriedades para um objeto sem ter que definir explicitamente seu tipo. Essa novidade surgiu na versão 3.0 junto com diversas outras tais como: propriedades automáticas, inferência de tipos, inicializadores de objetos, expressões lambda, métodos de extensão e outros.

Um tipo anônimo pode ser declarado da seguinte maneira:

var c = new { Nome = "Ari", Idade = 28 };

Neste caso a variável c é um tipo de possui duas propriedades: uma propriedade do tipo System.String chamada Nome e uma propriedade do tipo System.Int32 chamada Idade.

Os tipos anônimos são muito úteis quando realizamos consultas usando o LINQ pois normalmente temos que retornar um tipo com um conjunto de propriedades diferente dos tipos já existentes. Por exemplo, vejam as duas consultas abaixo:

var q1 = from c in dc.Customers
         where c.City == "London"
         select new { c.FirstName, c.LastName, c.City };

var q2 = from c in dc.Customers
         where c.City == "London"
         select new { c.FirstName, c.City };

A primeira consulta retorna um tipo que contém 3 propriedades (FirstName, LastName e City) e a segunda um tipo com 2 propriedades (FirstName e City). Sem os tipos anônimos teríamos que definir explicitamente duas classes para representar os tipos retornados.

Mas, se o tipo é anônimo, como podemos fazer para um método retorná-lo?

public ???? ListCustomers()
{
    var q1 = from c in dc.Customers
             where c.City == "London"
             select new { c.FirstName, c.LastName, c.City };

    return q1;
}

Bom, podemos resolver essa questão pensando da seguinte maneira: se um tipo anônimo é um tipo do .NET, então esse herda de System.Object, vamos então retornar um object.

public object ListCustomers() { ... }

Esse raciocínio está correto, o problema é que transformando para object perdemos a possibilidade de utilizarmos as propriedades do tipo anônimo diretamente (strong typing).

var c = ListCustomers();
c.FirstName = "Anders"; // erro

Bom, para resolver esse problema temos 3 possíveis soluções:

  1. Reflection.
  2. Dynamic Programming.
  3. Realizar um cast do object com o tipo anônimo.

A primeira opção é lenta e muito código deve ser criado, portanto será descartada. A segunda opção é interessante mas vou limitar meu escopo com as versões anteriores à 4.0. Vou mostrar então a terceira opção.

Para realizarmos um cast de um object para um tipo anônimo podemos, com auxílio de Generics, criar o método abaixo:

public T CastObjectToType<T>(object objeto, T tipo)
{
    return (T)objeto;
}

Podemos então converter o objeto em um tipo anônimo da seguinte maneira:

object o = ListCustomers();
var c = CastObjectToType(o, new { FirstName = String.Empty, 
                                  LastName = String.Empty, 
                                  City = String.Empty});
c.FirstName = "Anders";  // OK

Isso é possível graças à inferência de tipo, que nos permite fazer um cast sem conhecer o nome do tipo. É o que foi realizado no método CastObjectToType.

Finalizado, a resposta é SIM. Podemos retornar um tipo anônimo de um método.

Links de ajuda:

Anonymous Types (C# Programming Guide)
http://bit.ly/69HYJd

Generic Methods (C# Programming Guide)
http://bit.ly/6wFbas

Getting Started with LINQ in C#
http://bit.ly/4BOIUU

Asynchronous Method Invocation usando Delegates

Seguindo a sugestão do meu amigo Diego Azevedo vou demonstrar neste post como fazer a chamada de um método de forma assíncrona utilizando um padrão conhecido como Asynchronous Method Invocation (AMI).

Vou limitar o escopo desse post a um método de uma classe que possui somente um parâmetro e que retorna um valor.

Na maioria das linguagens de programação por padrão um método é executado de forma síncrona, ou seja, quando chamamos o método este é executado e somente quando termina é que a linha de execução do programa continua.

Por exemplo, no código abaixo a mensagem "Terminei !" só é mostrada quando o método FacaAlgumaCoisa é finalizado.

Classe objeto = new Classe();
objeto.FacaAlgumaCoisa();
Console.WriteLine("Terminei !")

Digamos que NÃO queremos que o usuário aguarde que o método FacaAlgumaCoisa termine para continuar a execução do programa. Queremos então estar aptos a utilizar o programa e fazer outras operações enquanto o método é executado. Temos então que desenvolver uma aplicação que seja multi-tarefa ou multi-threading em inglês.

Não quero entrar muito a fundo no conceito de multi-threading e deixo esse estudo como "dever de casa".

O código abaixo será uma aplicação console C# em Visual Studio 2008 Professional SP1 usando o .NET 3.5. É possível também utilizarmos a versão Express sem problemas.

Vamos começar criando uma classe qualquer que iremos utilizar em nosso exemplo. Para simplificar, vamos criar a classe no arquivo Program.cs mesmo.

public class MinhaClasse
{
    // exemplo de método que demora em torno de 10 segundos
    // para ser executado e retorna um valor
    public int MetodoDemorado(int a)
    {
        System.Threading.Thread.Sleep(10000);
        return a + 10;
    }
}

A classe é bem simples e contém um método que possui um parâmetro do tipo int e retorna o resultado da soma deste com o valor 10. O método estático Sleep da classe Thread foi introduzido no exemplo somente para simular um processamento que leva em torno de 10 segundos para executar.

Vamos declarar então no método Main um objeto da classe MinhaClasse.

// cria instância da classe MinhaClasse
MinhaClasse objeto = new MinhaClasse();

A idéia deste post é executar o método MetodoDemorado de forma assíncrona. Para executar um método de forma assíncrona precisamos de um delegate. Delegate é um tipo que referencia um método, similar aos ponteiros para funções do C/C++ (você já deveria saber disso !).

Felizmente no .NET 3.5 temos, com ajuda de Generics, alguns delegates que encapsulam alguns métodos. Neste exemplo vou utilizar o delegate System.Func pois preciso de um delegate que receba um inteiro e retorne um inteiro.

Criamos então a instância do delegate passando o método MetodoDemorado como parâmetro, conforme o código abaixo.


// encapsula o método em um delegate
Func<int, int> async = new Func<int, int>(objeto.MetodoDemorado);

Obs: Se o método não retornasse valor poderíamos utilizar o delegate System.Action.

Para executar o método de forma assíncrona vamos utilizar o método do delegate BeginInvoke gerado pelo compilador. Teremos que passar então como argumentos um inteiro, que é o parâmetro do método MetodoDemorado, um método callBack chamado quando o método finalizar a execução e um objeto que representa um estado qualquer.

async.BeginInvoke(...);

O argumento para o método callBack é uma expressão lambda que representa um delegate anônimo, ou seja, um delegate que recebe um parâmetro IAsyncResult e não retorna valor. No código acima, r é o parâmetro IAsyncResult. Além disso, estamos passando o valor 10 como argumento do método MetodoDemorado e null para o estado.

async.BeginInvoke(10, (r) =>
{
... // aqui é o corpo do callBack
}, null);

Utilizamos o método EndInvoke do delegate async para pegar o valor retornado pelo método MetodoDemorado. Após a execução do método, mostramos uma mensagem com o resultado.

// executa o método de forma assíncrona
async.BeginInvoke(10, (r) =>
{
    // resultado do método
    int resultado = async.EndInvoke(r);
    
    // mostra resultado
    Console.WriteLine("Resultado: {0}", resultado);

}, null);

Vamos incluir após o método BeginInvoke uma mensagem para que a aplicação console não seja finalizada e também vamos utilizar o método ReadKey que irá aguardar com que o usuário clique em uma tecla para finalizar a aplicação.

Segue abaixo portanto o código completo.

using System;

namespace MetodoAssincrono
{
    public class MinhaClasse
    {
        // exemplo de método que demora em torno de 10 segundos
        // para ser executado e retorna um valor
        public int MetodoDemorado(int a)
        {
            System.Threading.Thread.Sleep(10000);
            return a + 10;
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            // cria instância da classe MinhaClasse
            MinhaClasse objeto = new MinhaClasse();

            // encapsula o método em um delegate
            Func<int, int> async = new Func<int, int>(objeto.MetodoDemorado);

            // executa o método de forma assíncrona
            async.BeginInvoke(10, (r) =>
            {
                // resultado do método
                int resultado = async.EndInvoke(r);
                
                // mostra resultado
                Console.WriteLine("Resultado: {0}", resultado);

            }, null);

            Console.WriteLine("Aguardando execução do método...");
            Console.ReadKey(true);
        }
    }
}

Se executarmos essa aplicação, na tela de prompt será informado...



Só aqui já podemos perceber que a execução do método foi assíncrona, visto que a mensagem encontra-se após o BeginInvoke. Se aguardarmos um pouco (+- 10 segundos) poderemos ver a mensagem de retorno do método MetodoDemorado.



Espero ter sido claro e conseguido compartilhar com vocês esse tipo de técnica. Vou criar futuramente um post semelhante em uma aplicação Windows Forms tratando operações cross-thread.

Segue abaixo alguns links interessantes:

Delegate (C# Programming Guide)
http://bit.ly/54QbSD

Lambda Expressions (C# Programming Guide)
http://bit.ly/87xAZJ

Anonymous Delegates (C# Programming Guide)
http://bit.ly/6jZdYS

Func Delegate
http://bit.ly/5nAva4

Asynchronous Programming Using Delegates
http://bit.ly/6tcwzD

Must-Watch Videos – Anders Hejlsberg

Todo mundo já ouviu falar de role-model? É uma pessoa que serve de exemplo de comportamento. No mundo profissional, o meu role-model é Anders Hejlsberg.

anders Anders Hejlsberg (nome complicado de pronunciar e escrever) é Microsoft Technical Fellow e criador de ferramentas de desenvolvimento e linguagens de programação. Ele é também chief designer da linguagem C# e um dos principais participantes no desenvolvimento do .NET Framework.

Antes de trabalhar com C# e com o .NET Framework, Hejsberg foi o arquiteto de desenvolvimento do Visual J++ e do Windows Foundation Classes.

Hejlsberg foi um dos primeiros empregados da Borland e foi o autor original do Turbo Pascal, uma IDE revolucionária (era muito bom) e também chief architect do Delphi.

Na minha opinião, os feitos desse gênio modificaram a maneira com que trabalhamos com Frameworks e linguagens de programação.

Vou enumerar abaixo alguns dos vídeos que acredito serem obrigatórios para conhecer essa “figura”.  Os vídeos, além de mostrar a geniosidade de Anders, também é muito útil para entender conceitos sobre Generics, LINQ e Dynamic Programming.

1 - MSDN TV: Whiteboard with Anders Hejlsberg

No evento Tech-Ed de 2004 Anders responde as questões de alguns desenvolvedores e escreve alguns exemplos em um quadro. É interessante perceber a qualidade das perguntas e a maneira simples com que Hejlsberg as responde. Diversos assuntos são comentados, incluindo: generics, herança múltipla, parâmetros opcionais e outros.

É interessante perceber que na época ele não achava que os parâmetros opcionais, agora presentes no C# 4.0, seriam necessários, visto que podemos sobrecarregar métodos.

Link: http://bit.ly/4Z2qLe (Tamanho: 153MB)

2 – Channel 9: LINQ

Em um papo descontraído Anders fala sobre o LINQ, que é um conjunto de extensões para o .NET Framework que engloba operações de consulta, projeção e transformação integradas às linguagens. O LINQ foi uma inovação e tanto e também alterou a maneira de escrevermos código que realizam acesso a dados.

Data != Objects ? Com LINQ não !

Link: http://bit.ly/4AwCTg (Streaming)

3 – MSDN Spotlight: .NET Language Integrated Query (LINQ) Framework Overview

Esse vídeo é uma apresentação no Tech-Ed de 2007 e apesar de possuir alguns assuntos semelhantes ao vídeo #2 contém também explicações sobre o LINQ Framework, que inclui: LINQ to Objects, LINQ to Datasets, LINQ to SQL, LINQ to Entities e LINQ to XML.

Alguns demos sobre LINQ to Objects, LINQ to SQL e LINQ to XML são demonstrados. Vale a pena conferir.

Link: http://bit.ly/5sCNIe (Streaming)

4 – Channel 9: Future of C#

Esse é um dos meus vídeos favoritos e é uma apresentação no PDC de 2008 e que mostra a inclusão de Dynamic Programming no C#, parâmetros opcionais, compilador como um serviço e também a idéia de co-evolução entre as linguagens C# e VB.NET.

Link: http://bit.ly/6Tqq2k (Streaming)

Obs: No PDC de 2009 o Luca Bolognesi, responsável pelas linguagens C#, VB.NET e F# fez uma apresentação basicamente mostrando os mesmo assuntos. É um outro vídeo bem interessante.

Link: http://bit.ly/6FSA1X (Streaming)

Espero que gostem.