Quem gostou de ver o dinamismo trazido ao C# 4.0 vai gostar de ficar sabendo que ainda há alguns problemas na implementação não resolvidos pela Microsoft (quem não viu ainda eu já demonstrei o C# 4.0 dinâmico aqui no blog).

(Estou puxando do blog do Sam Ng. Se você prefere ler no original eu encorajo que o faça.)

Abaixo vou demonstrar alguns dos problemas atuais a serem resolvidos até o lançamento do C# 4.0 com relação ao dinamismo. São construções que no C# estático não chegam a causar a menor preocupação, mas no C# dinâmico trazem algumas rugas.

 

Alteração de valores em tipos dinâmicos:

Por causa de boxing e unboxing, e como todo tipo dinâmico é um objeto, esse tipo de operação:

static void Main()
{
    dynamic d = 10;
    d++;
}

Vai resultar em "d" com um valor igual a 10, e não 11. O valor é unboxed, alterado e não é trazido de volta. Já viu o bug no seu código, né? Isso é altamente contra-intuitivo.

Além disso, no CTP atual isso não funciona. Vejam o erro:

Dinamismo no C# pode ser um perigo

 

Alteração aninhada em tipos de valor:

O código a seguir também vai te enganar.

public struct S
{
    public int i;
}

public class D
{
    public S s;
    public static void Main()
    {
        dynamic d = new D();
        d.s = default(S);
        d.s.i = 10;
        Console.WriteLine(d.s.i);
    }
}

O valor escrito na console parece ser 10, mas na verdade é zero. Mesmo problema do item anterior, o valor de S é unboxed, e a variável "i" alterada na verdade é do objeto que foi unboxed, ou seja, o valor d.s.i ainda é zero. Maus um pau no seu código.

 

Nível de acesso dos membros:

Esse método vai falhar só porque é dinâmico:

public class C
{
    private void M(int x) { }

    static void Main()
    {
        dynamic d = 10;
        C c = new C();
        c.M(d);
    }
}

Se a variável "d" fosse um inteiro ele funcionaria. Ele falha porque, como d é uma variável dinâmica, a chamada a c.M é feita dinamicamente. Isso significa que em runtime o método M vai ser procurado e advinhem? como ele é privado não vai ser encontrado. Algo trivial ficou complicado. Resultado: bug, bug, bug. Na prática, o código nem compila.

Além disso, há outros problemas:

  1. Não é possível fazer chamadas em métodos que implementam interfaces explicitamente. Isso acontece porque o método é exposto somente via interface, e na classe que a implementa ele fica "escondido". Como uma chamada dinâmica depende da visibilidade do método, chamadas a métodos que implementam interfaces explicitamente vão resultar em métodos não encontrados, ou, em outras palavras: bugs.
  2. Não é possível chamar métodos de classe base com parâmetros dinâmicos, porque em runtime o método é chamado dinamicamente, e sempre vai resolver para a classe filha.

Tudo parecia tão simples, não é? É sempre bom lembrar que quando se trata de linguagens e compiladores nada é óbvio. A Microsoft vai resolver os problemas, só não sabemos ainda como. De qualquer forma fiquem atentos porque independentemente da forma que for escolhida para resolver esses casos, você vai ter que entender o que está acontecendo. E o resultado pode ser tudo, menos trivial. E se você não souber o que está acontecendo já viu: bug, bug, bug.

Minha sugestão? Testes unitários! Pelo menos se você não entender o que está acontecendo o teste vai te mostrar onde os bugs estão.


Postado na(s) categoria(s) .Net pelo giovanni bassi em 23 de dezembro de 2008 às 07:23 | Tags:

Ninguém avaliou. Dê sua nota!

  • Currently 0/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5

Este é o último post sobre C# 4, que vou mostrar o último pilar de mudanças propostas para o C# versão 4.0. Se você não viu os outros:

  1. C# 4.0 – Quanto antes melhor – onde apresentei um resumo das novidades, além de assuntos que envolvem a nova versão da linguagem;
  2. C# 4.0: Uma linguagem dinâmica – onde apresentei as novidades do C# 4.0 que vão aproximá-lo mais de uma linguagem dinâmicas.
  3. C# 4.0: Teremos covariância e contravariância – onde apresentei a tão esperada variância do C#.
  4. C# 4.0: Argumentos opcionais e argumentos nomeados – onde apresentei o que você ganha com argumentos nomeados e opcionais.

Para falar de Interop com COM, e das suas dificuldades, puxei da memória um post do Scott Hanselman sobre o assunto. Neste post ele apresenta a dificuldade de chamar um método COM, como uma automação do Excel, a partir do C#:

   ApplicationClass WordApp = new ApplicationClass();  
   WordApp.Visible = true;  
   object missing = System.Reflection.Missing.Value;  
   object readOnly = false;  
   object isVisible = true;  
   object fileName = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, @"..\..\..\NewTest.doc");  
   Microsoft.Office.Interop.Word.Document aDoc = WordApp.Documents.Open(  
       ref fileName,ref missing,ref readOnly,ref missing,  
       ref missing,ref missing,ref missing,ref missing,  
       ref missing,ref missing,ref missing,ref isVisible,  
       ref missing,ref missing,ref missing,ref missing);  
Se você utilizasse VB ficava bem mais fácil. Vejam o mesmo exemplo no VB (11 linhas viram 4 - se usasse With viravam 3):
Dim WordApp = New Microsoft.Office.Interop.Word.ApplicationClass  
WordApp.Visible = True    
Dim fileName = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "..\..\..\NewTest.doc")  
Dim aDoc As Document = WordApp.Documents.Open(FileName:=fileName, ReadOnly:=True, Visible:=True)  

O post do Scott se chama "Dim não é igual a var". Sugiro a leitura, é bem legal. Além disso o Scott escreve bem.

Pois bem, o que mudou?

Basicamente tudo. Agora com os argumentos opcionais, dos quais falei no último post sobre C# 4 tudo ficou mais fácil:

   ApplicationClass WordApp = new ApplicationClass();  
   WordApp.Visible = true;  
   object fileName = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, @"..\..\..\NewTest.doc");  
   dynamic aDoc = WordApp.Documents.Open(fileName, ReadOnly:false, Visible:true);

Ou seja, as mesmas 4 linhas que o VB já oferecia.

Há duas opções para resolver este problema. A primeira seria que, quando uma dll COM é referenciada, um método como o Open() receberia todos os argumentos com valores opcionais iguais a System.Reflection.Missing.Value, o que permitiria esta sintaxe. Analisei o código gerado com o Reflector (que já está atualizado para trabalhar com .Net 4.0: parabéns à Redgate), e percebi que toda a mágica acontece em tempo de compilação. Vejam o código gerado pelo compilador:

private static void Main(string[] args)
{
    ApplicationClass app = new ApplicationClass();
    object <>r__ComRefCallLocal0 = "teste";
    object <>r__ComRefCallLocal1 = Type.Missing;
    object <>r__ComRefCallLocal2 = Type.Missing;
    object <>r__ComRefCallLocal3 = Type.Missing;
    object <>r__ComRefCallLocal4 = Type.Missing;
    object <>r__ComRefCallLocal5 = Type.Missing;
    object <>r__ComRefCallLocal6 = Type.Missing;
    object <>r__ComRefCallLocal7 = Type.Missing;
    object <>r__ComRefCallLocal8 = Type.Missing;
    object <>r__ComRefCallLocal9 = Type.Missing;
    object <>r__ComRefCallLocala = Type.Missing;
    object <>r__ComRefCallLocalb = Type.Missing;
    object <>r__ComRefCallLocalc = Type.Missing;
    object <>r__ComRefCallLocald = Type.Missing;
    object <>r__ComRefCallLocale = Type.Missing;
    object <>r__ComRefCallLocalf = Type.Missing;
    Document doc = app.get_Documents().Open(
        ref <>r__ComRefCallLocal0, 
        ref <>r__ComRefCallLocal1, 
        ref <>r__ComRefCallLocal2, 
        ref <>r__ComRefCallLocal3, 
        ref <>r__ComRefCallLocal4, 
        ref <>r__ComRefCallLocal5, 
        ref <>r__ComRefCallLocal6, 
        ref <>r__ComRefCallLocal7, 
        ref <>r__ComRefCallLocal8, 
        ref <>r__ComRefCallLocal9, 
        ref <>r__ComRefCallLocala, 
        ref <>r__ComRefCallLocalb, 
        ref <>r__ComRefCallLocalc, 
        ref <>r__ComRefCallLocald, 
        ref <>r__ComRefCallLocale, 
        ref <>r__ComRefCallLocalf);
}

Ou seja, o compilador entendeu que é uma chamada COM e criou os Type.Missing por conta própria. Não é nada dinâmico, é estático com "syntatic sugar", como se diz…

Outras novidades interessantes (algumas que vocês devem ter notado neste rápido exemplo):

  1. Não há mais a necessidade do ref. (Isso é só para o COM, para o resto do C# continua precisando e é por causa do modelo de programação do COM que é muito particular.)
  2. Todos os métodos retornam dynamic, e não object.
  3. O código de interoperabilidade, que costumava ser gerado em outra dll, passa a ficar no próprio assembly da aplicação, e não mais no assembly externo como era antes. Na figura abaixo você pode ver a opção de "Embed Interop Assembly":
    Embed Interop Assembly no Visual Studio 2010

E também no Reflector você pode ver o Assembly misturado junto às outras classes (deixei expandido para destacar, notem o namespace Microsoft.Office.Interop.Word):

Reflector mostrando o assembly de PIA no meio do assembly da aplicação

O interessante é que, ao que tudo indica, o Visual Studio só coloca no assembly o que ele precisa. Neste caso foram apenas 3 interfaces. Acrescentei ao projeto uma referência ao componente do Excel, e ele não acrescentou ao assembly porque eu não estava utilizando. Inteligente!

Os dois últimos itens da lista anterior são opcionais, mas virão ligados por padrão.

E como todos os objetos do COM serão dynamic, isso significa que você faz tudo o que precisar com eles, como acessar indexadores, métodos e propriedades diretamente.

Mais um exemplo. Este código:

            var ExcepApp = new Microsoft.Office.Interop.Excel.Application();
            var chart = ExcepApp.ActiveWorkbook.Charts.Add();          

Gerou este resultado depois de compilado:

object charts = Activator.CreateInstance(
		Type.GetTypeFromCLSID(
			new Guid("00024500-0000-0000-C000-000000000046"))
	).ActiveWorkbook.Charts.Add(Type.Missing, Type.Missing, Type.Missing, Type.Missing); 

Notem como a compilação é inteligente: o objeto do Excel é instanciado a partir do seu class id, o GUID correto é passado, e partir desta instância é então criado um gráfico, passando-se argumentos Type.Missing. E o gráfico, como dito, é dinâmico, portanto do tipo object.

O Reflector também ajudou com o inlining, mas sem dúvida é um excelente exemplo do quanto um bom compilador faz a diferença.

É bom observar, tudo com relação à COM funciona com a máquina virtual disponibilizada pela Microsoft, ao contrário do que encontrei quando fui pesquisar as outras novidades do C# 4.0. Então, se você quiser ver isso tudo funcionando pode se animar.

Isso encerra a apresentação do C# 4.0. Espero que vocês tenham gostado. Vou falar ainda um pouco mais de C# 4.0, mas assumindo que todo mundo já sabe o que é!

Bom Natal a todos!


Postado na(s) categoria(s) .Net pelo giovanni bassi em 22 de dezembro de 2008 às 08:05 | Tags:

Ninguém avaliou. Dê sua nota!

  • Currently 0/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5

Ufa, já faz 2 semanas que eu falei de C# 4. Estou finalizando uma edição da .Net Magazine e o grupo de arquitetura, o .Net Architects, tem tomado meu tempo (para assuntos bem legais, é verdade, ainda assim…). Assim que o fechamento da .Net Magazine passar tenho um monte de coisas para escrever por aqui. Enfim, vamos lá.

Se você está chegando agora, este é o quarto post sobre o assunto. Se você não viu os posts anteriores:

  1. C# 4.0 – Quanto antes melhor – onde apresentei um resumo das novidades, além de assuntos que envolvem a nova versão da linguagem;
  2. C# 4.0: Uma linguagem dinâmica – onde apresentei as novidades do C# 4.0 que vão aproximá-lo mais de uma linguagem dinâmicas.
  3. C# 4.0: Teremos covariância e contravariância – onde apresentei a tão esperada variância do C#.

O assunto que vou tratar aqui hoje, argumentos opcionais e nomeados, é mais uma feature para o pessoal reclamar que o C# está ficando parecido com o VB. Se você está reclamando já adianto, C# e VB são linguagens irmãs, são dialetos de uma mesma lingua, e, segundo a Microsoft, vão ficar mais e mais parecido ao longo dos anos. Tudo que tiver em um vai ter no outro (o que for possível, ao menos). Não vou ficar falando que VB é uma linguagem tão profissional quanto C#, e que as duas têm prós e contras, porque já disse isso aqui antes, em um dos posts da série Polêmicas (para a qual já tenho uns 5 ou 6 assuntos guardados). Fato é: as linguagens todas vão ficar mais dinâmicas. É a influência do Ruby (e do LINQ) sobre o .Net. Você não precisa usar, mas vai perder um monte se não usar.

Na linha deste tipo de preocupação, na última reunião do grupo, o André Dias e eu estávamos conversando sobre essa história do C# vir com dinamismo, e que tem gente achando que a palavra-chave dynamic é igual a um "Dim". Não é. Depois aproveito e posto sobre isso também.

Indo então ao ponto: argumentos opcionais e nomeados. Antes, se você tinha um método, e quisesse que algum parâmetro fosse opcional, você fazia um overload:

    class Boliche
    {
        public void Derrubar(int pinos)
        {
        }
        public void Derrubar()
        {
            this.Derrubar(0);
        }
    }

Assim, chamar Boliche.Derrubar(), sem parâmetros, é o mesmo que chamar Boliche.Derrubar(0), passando zero. Funcionava perfeitamente, só que dava um trabalhão, principalmente se você tivesse um monte de overloads, além de não ser explícito para o consumidor da classe, que tinha que ficar escolhendo qual overload usar e não sabia qual o valor padrão. Afinal, o consumidor não tem acesso ao fonte necessariamente, e onde está escrito que o valor padrão é zero? Pois agora esse problema será resolvido. O C# utilizará uma sintaxe muito parecida com a do VB, onde, após a definição do nome do parâmetro, você coloca um igual e o valor padrão. No VB ainda precisa da palavra-chave "optional". No C# não precisa. É simples assim, e é uma mão na roda, livrando a gente daquele monte de overloads, para fazer algo tão simples:

    class Boliche
    {
        public void Derrubar(int pinos = 0)
        {
        }
    }

A chamada do cliente ao método fica idêntica.

Parâmetros opcionais não são nada de novo. Existe no .Net (e portanto no CLR e no CLS) desde a versão 1.0, já que eram necessários para o VB funcionar. A IL sempre gerou parâmetros opcionais para o VB. São "novidades" em uma linguagem com release de 8 anos, existente desde o VB com guaraná com rolha… Não entendo porque o C# não tem isso desde o princípio… vai ver o pessoal do Java e do C/C++ que estava migrando ia reclamar que C# era muito parecido com o VB. Vai saber…

Pois bem, isso são parâmetros opcionais. Indo em frente: Argumentos nomeados vêm de mão dadas com parâmetros opcionais. Vejam esse exemplo:

    class Boliche
    {
        public void Derrubar(int pinos = 0, int pinosRestantes = 10)
        {
        }
    }

Posso chamar direto Boliche.Derrubar(), mas isso passaria zero e dez aos parâmetros. E se eu quisesse chamar o método passando somente o segundo argumento, chamado pinosRestantes? Fácil:

        static void Main(string[] args)
        {
            var b = new Boliche();
            b.Derrubar(pinosRestantes: 7);
        }

Neste caso, estou dizendo ao compilador que quero passar somente o segundo parâmetro, e que no primeiro aceito o valor padrão, que é zero. Também muito simples.

Algumas regras:

  1. Parâmetros opcionais são sempre os últimos. Você não pode ter um parâmetro não opcional à direita de um opcional. Faz sentido se você pensar na estruturação da sintaxe.
  2. Somente constantes são aceitas como valores padrão de parâmetros opcionais. Isso pode ser um bom motivo para continuar a usar overloads em alguns casos.
  3. Argumentos nomeados podem ser especificados em qualquer ordem. Você poderia chamar, por exemplo, Boliche.Derrubar(pinosRestantes: 5, pinos: 3), sem problemas. Mas ainda que possam ser especificados em qualquer ordem, eles serão avaliados na ordem escrita no método que está chamando, e não na ordem declarada na função. Isso é importante caso você esteja chamando funções para passar valor aos parâmetros, como Boliche.Derrubar(pinosRestantes: this.ObterPinosRestantes(), pinos: this.ObterPinosDerrubados()). Neste caso, o método ObterPinosRestantes é chamado antes do método ObterPinosDerrubados. Isso pode ser um problema caso você tenha funções com efeitos colaterais, algo que deve ser evitado a todo custo (não crie uma função que retorna um valor, e altera outro no contexto quase de forma escondida, a não ser que queira problemas para depurar depois).
  4. Você pode especificar argumentos não opcionais por nome normalmente. Isso não está restrito a argumentos opcionais.

Essas features são plenamente suportadas no CTP do Visual Studio 2010 que saiu em Outubro, ao contrário das features de variância e dinamismo, que estão pela metade.

No próximo post de C# 4.0 vou falar de interop com COM, e vocês vão ver o quanto isso ajuda neste tipo de caso.


Postado na(s) categoria(s) .Net pelo giovanni bassi em 1 de dezembro de 2008 às 19:08 | Tags:

Ninguém avaliou. Dê sua nota!

  • Currently 0/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5

Continuo trazendo as novidades de C# 4.0. Este é o terceiro post, se você não viu os posts anteriores:

  1. C# 4.0 – Quanto antes melhor – onde apresentei um resumo das novidades, além de assuntos que envolvem a nova versão da linguagem;
  2. C# 4.0: Uma linguagem dinâmica – onde apresentei as novidades do C# 4.0 que vão aproximá-lo mais de uma linguagem dinâmicas.

Ou ainda pela tag: C#4.

Aviso: Antes de continuar, vou contar novamente a história triste (já contada nos posts anteriores sobre o mesmo assunto). Os bits que estão disponíveis para download não são tão quentes quanto os bits apresentados no PDC. Sorry, mas é verdade. Isso quer dizer que algumas das coisas que vou mostrar aqui não vão compilar no build do CTP. Sabendo disso, vamos lá.

Neste post vou falar de covariância e contravariância, o que finalmente vai por um fim à invariância predominante no C# (fora os arrays que já são covariantes). É o segundo grande assunto sendo abordado porque é o segundo mais legal e interessante na minha opinião. A princípio é meio contra-intuitivo pensar sobre variância, mas depois que sua mente já deu uns nós ela volta ao normal e de repente você começa a entender. Se acontecer com você, não se preocupe, é assim mesmo, é normal.

Para entender um pouco melhor o drama da variância, dêem uma lida em uma reclamação minha sobre o problema gerado por ela aqui no blog. Vale a pena também ver o trabalho do Felipe Pessoto, que traduziu uns posts do Eric Lippert (do time do C#, e um ótimo blogger) que tratavam muito bem sobre o assunto. Se você não conhece o assunto eu recomendo a leitura.

Lembrando então rapidamente> Arrays são covariantes. Isso significa que você pode pegar um array de strings:

string[] textos; 

E tipá-lo como um array de objetos, porque string herda de objeto:
object[] objetos = textos; //compila sem problemas

Mas tipos genéricos são invariantes. Isso quer dizer que se você tiver uma lista de strings:

IList<string> textos = new List<string>(); 

Você não consegue passá-la para uma lista de objetos:
IList<object> objetos = textos; //não vai compilar no C# 3 

Ainda que toda string seja um objeto também, o problema acontece porque IList<string> não herda de IList<object>, e portanto a associação não é permitida. Se fosse, você poderia quebrar o tipo genérico em runtime assim:
IList<object> objetos = textos; 
objetos.Add(new Button()); //pau em runtime: um botão não é uma string

De fato, é exatamente o que pode acontecer com arrays. Seguindo no exemplo, se você fizer isso:

objetos[0] = new Button(); 

Vai compilar porque um botão é um objeto, mas em runtime você vai ter um exceção sendo lançada, porque um botão não é uma string, e na verdade o array é originalmente um array de strings, apenas tipado como um array de objetos. Perigoso, certo?

Ok, mas imaginem que tivéssemos um enumerador de strings:

IEnumerable<string> textos = ObterUmEnumeradorDeStrings(); 

Não seria tão perigoso assim associar este enumerador a um enumerador de objetos:
IEnumerable<objects> objetos = textos; 

Porque, na verdade, não temos como alterar os tipos contidos pelo enumerador, ele é uma coleção em que os itens só podem ser lidos e iterados, e é por isso somente-leitura. Esse tipo de conversão não é perigosa, e não teria problema algum em ser permitida, certo?

Na verdade, o que acontece com IEnumerable<T>, é que este tipo genérico apenas devolve o tipo T em operações, ele não o recebe como parâmetro em nenhum momento, o que elimina a necessidade de qualquer conversão. Sua assinatura é assim:

    public interface IEnumerable <T>: IEnumerable
    {
        IEnumerator<T> GetEnumerator();
    }

É diferente de IList, que recebe T como parâmetro, por exemplo, nos métodos Insert e IndexOf (além de outros das interfaces que IList implementa):

    public interface IList<T> : ICollection<T>, IEnumerable<T>, IEnumerable
    {
        T this[int index] { get; set; }
        int IndexOf(T item);
        void Insert(int index, T item);
        void RemoveAt(int index);
    }

Por este motivo, no caso de IList, o objeto não seria covariante com segurança, porque se isso fosse possível, um IList<object> poderia, no fundo, ser um IList<string> e receber uma chamada de Insert(0, new Button()), o que resultaria em um erro em runtime.

Para indicar este tipo de situação em tempo de compilação, o time do C# incluiu duas novas palavra-chave opcionais na definição de generics: "out" e "in". "Out" é para ser usado quando o tipo genérico é covariante, e pode ser retornado em alguma função ou propriedade readonly, como no caso do IEnumerable. "In" é para quando o tipo genérico é contravariante, e pode ser recebido como parâmetro.

Assim, a interface IEnumerable na nova versão ficaria assim:

    public interface IEnumerable <out T>: IEnumerable
    {
        IEnumerator<T> GetEnumerator();
    }

E você poderia então fazer isso:

IEnumerable<string> textos = ObterEnumerador(); 
IEnumerable<object> objetos = textos; //vai compilar no C# 4 

Porque o compilador sabe que é seguro. Um enumerador de strings é sempre um enumerador de objetos e não há perigo em tratá-lo pelo tipo menos específico (objeto). Muito inteligente, certo?

Um exemplo de contravariância é uma interface que só recebe parâmetro de entrada. O exemplo dado pela própria Microsoft foi a interface IComparer. Originalmente assim:

    public interface IComparer<T>
    {
        int Compare(T x, T y);
    }

Ela poderia ser alterada com a palavra chave "In", e ficaria assim:

    public interface IComparer<in T>
    {
        int Compare(T x, T y);
    }

O que permitiria isso, que até o momento não é permitido no C# 3.0:

IComparer<object> comparadorObjetos;
IComparer<string> comparadorStrings = comparadorObjetos;

Isso porque uma classe que é capaz de comparar objetos, também é capaz de comparar strings. Por esse motivo, a interface é marcada como genérica e contravariante, ou seja, aceita tipos genéricos apenas como entrada em formato de algum parâmetro, mas não como saída (de uma função, por exemplo).

Isso conclui o básico sobre a novidade de variância. Faço apenas três observações:

  1. Assim como no caso dos tipos dinâmicos, a máquina virtual disponibilizada para download pela Microsoft com Visual Studio 2010, Rosario, C# 4.0 e .Net 4.0 não contém ainda os bits necessários para fazer essa sintaxe compilar. Isso quer dizer que não dá ainda para criar tipos variantes em C# 4.0 com essa VM, e não há os tipos IEnumerable<out T>, ou IComparer<in T> no BCL. Vamos ter que esperar a próxima verão sair, e, depois de todo esse auê com C# 4.0, espero que não demore tanto.
  2. O segundo aviso é que tipos de valor, como tipos primitivos e structures, são sempre invariantes, ou seja, não dá para fazer cast de um IEnumerable<DateTime> para IEnumerable<object>.
  3. O terceiro aviso é que parâmetros passados como "out" ou "ref" não podem ser variantes, porque eles seriam "in" e "out", e acabam não sendo nenhum dos dois.

Boa parte do .Net Framework 4.0 foi alterado para acomodar essa grande mudança. Além dos já citados IEnumerable e IComparer, também há outros, como IQueryable<out T>, e também delegates, como Func<in T, out R>, Action<in T>, e Predicate<in T>. Notem que Func tem um tipo genérico "in" e outro "out". Isso tem cara que vai deixar muita gente confusa… No fim das contas, a maior parte das pessoas vai simplesmente usar e nem se preocupar como isso tudo está funcionando.

O negócio vai ser tão automático que, quando estivermos trabalhando com C# 9.0, em, sei lá, 2020, alguém vai falar algo do tipo:
- Lembra quando C# era totalmente invariante com generics?
E algum newbie vai dizer:
- C# já foi invariante?

Ah… o futuro…


Postado na(s) categoria(s) .Net pelo giovanni bassi em 17 de novembro de 2008 às 19:16 | Tags:

Ninguém avaliou. Dê sua nota!

  • Currently 0/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5

Seguindo na série de novidades do C# 4.0 que vou abordar aqui no blog, quero agora dar uma olhada no dinamismo adicionado à linguagem.

Aviso: Antes de continuar, gostaria apenas de contar para vocês uma história triste. Os bits que estão disponíveis para download não são tão quentes quanto os bits apresentados no PDC. Sorry, mas é verdade. Isso quer dizer que algumas das coisas que vou mostrar aqui não vão compilar no build do CTP, e por isso, eu posso até errar alguma vírgula, já que não vou ter confirmação do compilador ou testes. Enfim, é o que dá trabalhar na borda das novidades de tecnologia, faz parte… Então vamos lá.

Não foi nesta versão (e não vai ser nunca) que o C# se tornou uma linguagem dinâmica como o Python ou o Ruby (implementados no .Net como IronPython e IronRuby). Mas agora, em poucas palavras, é possível ter uma variável tipada como "dynamic", e chamar sobre ela qualquer método. Isso quer dizer que quando você coloca o ponto (.), nada aparece. Nada. E você pode digitar o que quiser depois do ponto. E compila. E esse negócio todo vai ser resolvido só na hora que o aplicativo estiver rodando (em runtime). Ou seja, não há verificação em tempo de compilação, o que é um dos contras de linguagens dinâmicas. Mas há vários pros, então não se preocupe. Além disso, você só usa se quiser.

Você pode resolver esse problema de não ter verificação em tempo de compilação com a utiliação de testes unitários! Muito bem, testes, testes, testes! Crie muitos testes. (Aliás, crie testes mesmo se você não for trabalhar com C# e suas capacidades dinâmicas. Testes são bons, fazem você crescer mais forte, morrer mais tarde, casar com uma pessoa legal, e além disso previne espinhas. E dá muita sorte.)

É muito parecido com o que o VB faz com Late Binding, mas é mais poderoso. Isso porque permite integrar vários modelos de chamada dinâmica, como Javascript, POCOs, COM, e outros que você quiser, já que é extensível.

Por exemplo, se estiver trabalhando estático, segue abaixo o código simples de uma calculadora:

    class Calculadora
    {
        public long Adicionar(int numero1, int numero2)
        {
            return numero1 + numero2;
        }
    }

Você faz sua soma em um windows form desta forma:

        private void butSomar_Click(object sender, EventArgs e)
        {
            Calculadora calc = new Calculadora();
            int numero1 = Convert.ToInt32(txtNumero1.Text);
            int numero2 = Convert.ToInt32(txtNumero2.Text);
            long resultado = calc.Adicionar(numero1, numero2);
            lblResultado.Text = "Resultado: " + resultado;
        }

Até aí tudo bem. Mas e se for dinamicamente? E se eu não souber o tipo? Bom, o C# de hoje já permite isso, com Reflection, e em grande parte é o que VB faz por baixo dos panos quando você faz late binding. Fica assim:

private void butSomarQuaseDinamico_Click(object sender, EventArgs e)
{
    object calc = new Calculadora();
    Type type = calc.GetType();
    int numero1 = Convert.ToInt32(txtNumero1.Text);
    int numero2 = Convert.ToInt32(txtNumero2.Text);
    long resultado = (long)type.InvokeMember("Adicionar",
        System.Reflection.BindingFlags.InvokeMethod,
        null,
        calc,
        new object[] { numero1, numero2 });
    lblResultado.Text = "Resultado: " + resultado;
}

Maior trabalhão, certo? Veja com C# 4.0 e tipos dinâmicos como isso facilita:

Novo método de adicionar:

        public dynamic Adicionar(dynamic numero1, dynamic numero2)
        {
            return numero1 + numero2;
        }

E novo método de chamada:

        private void butSomarDinamico_Click(object sender, EventArgs e)
        {
            dynamic calc = new Calculadora();
            dynamic numero1 = txtNumero1.Text;
            dynamic numero2 = txtNumero2.Text;
            long resultado = calc.Adicionar(numero1, numero2);
            lblResultado.Text = "Resultado: " + resultado;
        }

Bom, apesar de parecer que vai funcionar, na verdade, não vai. Alguns problemas: apesar de a avaliação de tipos dinâmicos ser feita em runtime, ainda assim o tal do tipo dinâmico ainda tem um tipo. Declará-lo como dinâmico significa apenas que o compilador não vai checar suas chamadas, mas em runtime isso vai ser feito. Neste caso, as variáveis numero1 e numero2, declaradas dinâmicas, são na realidade strings. E ao chamar o operador "+" sobre strings, o que acontece é uma concatenação, e não uma soma. Para resolver isso, você deve voltar a converter. Assim:

        private void butSomarDinamico_Click(object sender, EventArgs e)
        {
            dynamic calc = new Calculadora();
            dynamic numero1 = Convert.ToInt32(txtNumero1.Text);
            dynamic numero2 = Convert.ToInt32(txtNumero2.Text);
            long resultado = calc.Adicionar(numero1, numero2);
            lblResultado.Text = "Resultado: " + resultado;
        }

Dessa forma, a variavel numero1 é declarada dinâmica, mas no fundo, ela é um inteiro. Isso mostra o poder do dinamismo: o operador é chamado sobre o tipo que você passou, qualquer que seja ele. Se for uma string ele concatena, se for um inteiro ele soma. Isso é muito poderoso.

Observação: Não se anime muito com esse exemplo ainda, porque esse é um caso típico de bits não disponíveis ainda. Esse código vai dar um erro de compilação na linha que diz "return numero1 + numero2;", porque os bits do CTP não implementaram ainda resolução de operadores. Esse exemplo da o seguinte erro: "Operator '+' cannot be applied to operands of type '::dynamic' and '::dynamic' ". O próximo CTP (ou já o Beta) vai ter isso funcionando.

O mais engraçado disso tudo é que o C# continua sendo uma linguagem estática, mas com "aspectos" dinâmicos. A variável numero1, por exemplo, é "estáticamente tipada como dinâmica". Ela possui um tipo, e esse tipo é "dynamic". E esse tipo tem naturezas dinâmicas, e é tratado de forma diferente pelo compilador. Mas no fim das contas, um tipo dinâmico é um object com tratamento especial em compilação e em runtime. É isso.

Outra coisa importante de saber é que operações com objetos dinâmicos sempre retornam outro objeto dinâmico. Assim, se você chamar:

        public dynamic Somar10(dynamic numero)
        {
            var ret = numero + 10;
            return ret;
        }

A variável ret vai ser de tipo dinâmico. Tipos dinamicos se propagam, então tenha cuidado.

Acho que isso já dá pra entender o que é um tipo dinâmico. Pegando os exemplos da Microsoft, veja o que mais você pode fazer:

            dynamic d = RetornaObjetoRemoto();
            //chamando um método:
            d.M(7); 
            // setando propriedades:
            d.f = d.P; 
            // indexadores funcionando dinamicamente
            d["one"] = d["two"]; 
            // operadores:
            int i = d + 3; 
            // objeto dinamico é na verdade um delegate
            string s = d(5,7); 

É claro que, neste caso, não seriam todas as operações possíveis, por causa do tipo. Você não tem indexadores em delegates, por exemplo… Mas esse código deve compilar com a versão final do C# 4.0 (a do CTP da erro nos operadores – novamente - e mais um erro nos indexadores - "Cannot apply indexing with [] to an expression of type '::dynamic' ").

Outra coisa interessante é como é feita a resolução de uma chamada a um método que possui sobrecargas, com tipos dinâmicos. Assim, por exemplo, se tivermos um método sobrecarregado desta forma na nossa calculadora:

        public DateTime Adicionar(DateTime data, int dias)
        {
            return data.AddDays(dias);
        }

(Lembrem-se, já havia um outro método Adicionar que aceitava dois inteiros.)

Ao chamar a função Adicionar em um tipo dinâmico, ele vai ser resolvido de acordo com os valores de runtime. Assim:

            dynamic calc = new Calculadora();
            calc.Adicionar(1, 2);
            calc.Adicionar(DateTime.Now, 2);

A primeira chamada a Adicionar vai chamar o overload Adicionar(int, int), e a segunda vai chamar Adicionar(datetime, int). E essa resolução é feita em toda pelo framework. Legal, não é?

Se você quiser ver uma aplicação legal, veja o vídeo em que o Anders Hejlsberg apresenta o C# 4.0, mais ou menos no minuto 26, e você verá uma aplicação Silverlight que utiliza o Virtual Earth e integra C# no code-behind e javascript. No Javascript há isso:

Código Javascript de chamada do Virtual Earth

Note que Javascript é uma linguagem bastante dinâmica, e não há tipos declarados.

E no código em C#, há a chamada ao método javascript, através da Interoperabilidade (a parte: só o fato do Silverlight permitir essa interoperabilidade é o máximo, não é?):

Código C# do Silverlight que chama o código Javascript anterior

Vocês notaram no código C# as chamadas todas são feitas de uma maneira um pouco estranha, através de métodos "Invoke" ou "SetProperty". Detalhe, tudo isso já é possível hoje, com C#3 e Silverlight.

Pois bem, o Anders começa então a aplicar um pouco do dinamismo. Ele aplica o método de extensão AsDynamic, que retorna estes tipos dinâmicos, nos dois objetos que eram trabalhados, um HtmlDocument e uma HtmlWindow. E faz então alguns ajustes, chamando diretamente os métodos que eram chamados via "Invoke", e setando propriedades sem "SetProperty". Fica assim:

Código C# do Silverlight modificado para usar as propridades dinâmicas do C# 4.0

Bem mais legível, certo? E como os métodos eram chamados com strings, e não havia verificação em tempo de compilação, nada mudou com relação a isso.

Em seguida, ele recorta o código Javascript, cola ele no código C# e faz alguns ajustes. Bem poucos, na verdade, e fica assim:

Código C#, migrado do antigo código Javascript, e todo trabalhado dinamicamente

A partir daí, este código C# passa a funcionar. Tudo dinâmico, e até meio "mágico". É nesse tipo de aplicação, na interação com linguagens dinâmicas, ou objetos desconhecidos, que o custo das linguagens dinâmicas se paga.

É importante lembrar que todo este código traz uma dependência sobre o DLR, que é o Dynamic Language Runtime. O DLR vai vir junto com a próxima versão do .Net Framework e ainda não está concluído. Este fato, inclusive, é um dos problemas do pessoal do IronRuby, que também dependem do DLR, mas só podem progredir se o DLR já tiver implementado algumas coisas (o IronRuby é um projeto opensource). Além disso, por enquanto tenho ouvido falar de problemas de performance no DLR, o que está sendo endereçado pela Microsoft, e tudo isso leva tempo.

Assim que sair o novo CTP do VS 2010 e o suporte a tipos dinâmicos estiver implementado eu volto a falar do assunto. Enquanto isso, vou seguir nas outras novidades do C# 4.0.

Vocês gostaram dessa novidade? Onde mais vêem aplicações reais dessa nova funcionalidade do C#?


Postado na(s) categoria(s) .Net pelo giovanni bassi em 11 de novembro de 2008 às 16:59 | Tags:

5.0 ponto(s). Avaliado por 1 pessoas

  • Currently 5/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5

Já sei o que você está pensando. 4.0? É. 4.0.
- Mas eu nem me acostumei com o 3.0 ainda!
Paciência. Mas também não é pra já. A Microsoft fala 2010, mas acho que até Dezembro do ano que vem eles liberam, no finalzinho do ano, como fizeram com o 2008, que na verdade saiu em Novembro de 2007.

Eu não tinha visto direito, mas a máquina virtual com o CTP do Visual Studio 2010 (falei dela aqui) já vem também com o C# 4.0 e o VB X. Pra que… Já estou olhando e estou gostando do que estou vendo. Começa aqui então uma série sobre novidades no C# 4.0. São 4 as grandes novidades do C# 4.0:

  1. Tornar o C# uma linguagem com capacidades de uma linguagem dinâmica;
  2. Trazer variância ao C#, tanto contravariância quanto covariância.
  3. Argumentos opcionais e argumentos nomeados em funções;
  4. Novidades na interação com o COM (incluindo parte das novidades do dinamismo do ponto 1);

Eu gostei muito das novidades, em especial as duas primeiras. A primeira vai deixar o C# muito parecido com o VB, que sempre permitiu late binding, mas ela é mais do que isso. A segunda é sensacional, porque esse lance de o C# ser invariante já me incomodou muito (mais info sobre variância e porque isso é um problema aqui). A solução dada para a variância é muito elegante.

Outra coisa interessante é que finalmente a Microsoft parou com esse negócio de trazer uma feature para o VB e não trazer a mesma feature para o C# e vice-versa. Agora, segundo Anders Hejlsberg, o principal criador do C#, as duas linguagens vão evoluir em paralelo, e o que for adicionado à uma, provavelmente vai ser adicionado à outra. (Será que o VB vai permitir finalmente chamadas unsafe?) Isso significa algo que já venho dizendo a muito tempo: VB e C# são dialetos de uma mesma lingua. Se você quer ver uma linguagem realmente diferente vai dar uma olhada IronRuby ou F#.

Já estudei todos os pontos, mas quero apresentá-los com calma, então no próximo post inicio provavelmente falando de variância, que é um tema que gosto muito. Muito legal também o compilador dinâmico de C# apresentado pelo Anders Hejlsberg em uma palestra no PDC 2008, em que ele demonstra um novo compilador feito em C# (o atual é feito em C++), e o utiliza para compilar código sob demanda. Na boa, o público veio abaixo na hora em que ele fez a demo, muito legal. Só para vocês terem uma idéia das possibilidades, vejam um código simples;

CSharpEvaluator ev = new CSharpEvaluator();
ev.Usings.Add("System");
ev.Eval("for (int i = 0; i < 10; i++) Console.WriteLine(i * i)");

Explicando:

É uma aplicação console. Na linha 1 é criada uma instância do compilador, que é representado por uma classe, como qualquer outra. Na linha 2 ele adiciona uma declaração de using. Na linha 3 ele manda o compilador avaliar uma string, que executa, e, acreditem, escreve na console. Esse é um exemplo simples, o cara cria logo em seguida, com poucas linhas de código, um avaliador direto via console, que avalia o que o usuário escreve e executa, como se fosse um command prompt de C#. Ele chega a criar um form, adicionar um botão, e adicionar o endereçador do botão ao ser clicado, e exibir um Hello World no clique. Lindo lindo lindo. Mas esse não sai tão cedo. Vejam esse trabalho com imagens que tirei do vídeo, com o tal command prompt de C# que ele fez na hora:

Executando uma conta de seno, e um for loop sob demanda:
Executando uma conta de seno, e um for loop sob demanda

Aqui ele declara uma função sob demanda, e chama ela em seguida:
Aqui ele declara uma função sob demanda, e chama ela em seguida

Aqui ele cria um form, coloca uma imagem e um botão:
Aqui ele cria um form, coloca uma imagem e um botão

E por fim ele adiciona um event handler e clica, mostrando o resultado na janela.
E por fim ele adiciona um event handler e clica, mostrando o resultado na janela

Lembrando que isso tudo foi feito a partir de strings, compiladas sob demanda. Muito legal!

Só ficou faltando um toque de Spec#. Será que não vamos ganhar esse brinquedo, nem como opcional?

Mais info sobre tudo isso no blog do Charlie Calvert, neste documento oficial do Word sobre novidades do C#, no video do Anders Hejlsberg do PDC 2008, em dois vídeos (aqui e aqui) com o time do C#, na página do Codeplex sobre futuro do C#, ou aqui, em breve, e em português :) A VM com Visual Studio 2010 e C# 4.0 você baixa aqui (ela tem 8GB de download compactado, e mais de 20GB descompactado).


Postado na(s) categoria(s) .Net pelo giovanni bassi em 6 de novembro de 2008 às 20:08 | Tags: ,

Ninguém avaliou. Dê sua nota!

  • Currently 0/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5

Brain3 Como a cada hora estou realizando consultoria em um cliente diferente, e o mercado é totalmente dividido entre C# e VB, eu tenho que escrever código nas duas linguagens o tempo todo. Muitas vezes isso acontece no mesmo dia. Se juntar o trabalho de edição da .Net Magazine, onde vejo de tudo: C#, VB, e até J# (e logo logo F#), a situação complica ainda mais.

Como as linguagens são muito parecidas, geralmente não é um problema, mas no fim do dia, quando isso acontece muito… lá estou eu, colocando ponto e vírgula no VB, parênteses no C#, ou esquecendo que no C# o operador de identidade tem dois iguais (==). Às vezes demora uns 3 ou 4 minutos para cair a ficha e entender onde está o erro, afinal, a aplicação não compila.

Agora, vocês já imaginaram como isso vai ser em 2 ou 3 anos? Vou ajudar:

  1. C#
  2. VB
  3. J#
  4. F#
  5. Iron Ruby
  6. Iron Python
  7. M (do OSLO)

Isso sem falar em uma ou outra linguagem independente por aí, como o Boo (esse é o nome da linguagem mesmo, não estou tentando te assustar), que pode acabar ganhando o mainstream do dia pra noite. E sem contar também as outras linguagens que não são diretamente dependentes do CLR, ou do DLR, como:

  1. Javascript
  2. HTML
  3. CSS
  4. XAML

Ou seja, não preciso mais me preocupar em errar ao colocar chaves no VB de vez em quando. Daqui uns anos o ambiente de trabalho vai estar tão complicado, que vou precisar de uns 5 minutos só pra "carregar" a linguagem do momento, bem ao estilo Matrix. Quando falamos que devemos usar a linguagem certa para cada tipo de aplicação, não estamos brincando, não é verdade?

Depois disso tudo, ambidestria fica até parecendo fácil.


Postado na(s) categoria(s) .Net , Reclamações pelo giovanni bassi em 28 de outubro de 2008 às 15:53 | Tags: ,

Ninguém avaliou. Dê sua nota!

  • Currently 0/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5

Não é agora que eu vou apagar a minha máquina virtual com o Visual Studio Rosario. Dando uma olhada no release do Visual Studio 2010 já adianto pra vocês que as ferramentas de arquitetura que eu falei aqui não estão lá. Não sei porque, mas simplesmente não estão. É uma pena, porque o grande público vai seguir sem ver as excelentes ferramentas. Porque será?

Agora, que é legal ver o 4.0 entre os frameworks disponíveis, ah, isso é:

Visual Studio 2010

Update: Não vem não. Elas estão lá, só mudaram de lugar. Procure em File > New Project > Modeling Projects > Empty Project. Está tudo lá. Obrigado André pelo toque.


Postado na(s) categoria(s) Visual Studio , .Net pelo giovanni bassi em 28 de outubro de 2008 às 12:17 | Tags: ,

Ninguém avaliou. Dê sua nota!

  • Currently 0/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5

Novo logo do .Net

Acabo de ver no blog do Scott Hanselman que o .Net tem um novo logo. Mais informações do porque do novo logo aqui.

Gostei? Hm… não sei. Às vezes até eu sou avesso a mudanças, mas não dá para dizer que o novo logo não é bonito. Me acostumei com o antigo, todo colorido, e esse novo realmente parece melhor, mas costume é costume, e vou ter que me acostumar com essa novidade.

E vocês, o que acharam?


Postado na(s) categoria(s) .Net pelo giovanni bassi em 25 de outubro de 2008 às 09:47 | Tags:

Ninguém avaliou. Dê sua nota!

  • Currently 0/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5

Eu já havia visto antes, já faz um tempo, e quando vi pensei “Ah, nunca vou ter esse problema.” Pois é, tive. Talvez você também tenha, e como não encontrei muitos recursos em português vai aqui a dica, da maneira mais simplificada que eu consegui:

Herança não funciona com generics. Nem no C#, nem no VB.

- Hã, como assim?

Mais ou menos assim: você tem uma lista de cachorros. Todo cachorro é um mamífero (cachorro herda de mamífero). Você consegue passar uma variável cachorro para uma outra declarada como mamifero:

var cachorro = new Cachorro();

Mamifero mamifero = cachorro;

Nada de mais, certo? E esse código a seguir, funciona? O que você acha?

var cachorros = new List<Cachorro>();

List<Mamifero> mamiferos = cachorros;

Faz sentido que funcionasse, certo? Mas não funciona. Um screenshot do Visual Studio mostra a vocês o erro:

Erro de covariância no Visual Studio (C#)

O erro é “Cannot implicitly convert type 'System.Collections.Generic.List<InvariantsCSharp.Cachorro>' to 'System.Collections.Generic.List<InvariantsCSharp.Mamifero>'”. E nem adianta fazer cast, porque não vai poder. O compilador é muito esperto. E se, de alguma forma, você conseguir, vai dar pau em runtime, na hora da associação. E porquê? Se todo cachorro é um mamífero, uma lista de cachorros é uma lista de mamíferos, certo?

Não. Errado. Na verdade, não é bem uma questão de lógica, mas de especificação. O CLR permitiria isso, quem não permite são o C# e o VB. Primeiro temos que entender que List<Cachorro> não herda de List<Mamifero>. Mesmo se fosse possível, não seria por herança. Entender isso é o primeiro passo.

Em poucas palavras, o motivo disso não ser permitido é o seguinte: Se você pudesse converter uma lista de cachorros para uma lista de mamíferos, poderia, depois, adicionar um gato à lista. E o compilador jamais iria pegar esse problema, você só ia descobrir o problema em tempo de execução. E o grande motivo de existirem os generics é justamente você poder ter segurança do tipo em tempo de compilação. Já imaginou Se isso fosse possível? Dá uma olhada no que poderíamos fazer:

var cachorros = new List<Cachorro>();

List<Mamifero> mamiferos = cachorros;

Gato gato;

mamiferos.Add(gato);

Se isso fosse possível, seria pau na certa. E só em runtime.

Essa especificação, que impede esse tipo de construção, se chama “invariante”. Generics, no C# e no VB, são invariantes.

Mas o problema de listas ainda dá para resolver, só que sem generics. Se você quiser criar uma lista de cachorros, e depois passá-la para uma variável de lista de mamíferos, use um array. A seguinte construção é perfeitamente legal:

Cachorro[] cachorros;

Mamifero[] mamiferos = cachorros;

Agora, já viu se você adicionar um gato nesse array de mamíferos, não é? É pau na hora. O que fizemos foi simplesmente passar um array já criado para outro sendo declarado em um tipo menos específico. Essa possibilidade se chama “covariância”. Também existe a possibilidade de amplicação, chamada contravariância. Penso em possibilidades legítimas de uso, mas tem que ser feito com cuidado. Além disso tudo, arrays são construções que hoje, com os generics, ficaram velhas. Vejam este post do Eric Lippert, feito esta semana, sobre isso. Ele bem nos lembra que arrays não são redimensionáveis, e não são classes, são estruturas, o que nos impede, por exemplo, de deixá-los read-only. Leia o post, é bem legal.

Se a necessidade não for com listas você vai morrer na praia. Não há outra solução. Acreditem, me deparei com isso hoje, e não tem jeito. O compilador não permite em tempo de compilação, e em runtime dá o mesmo pau, se você der um jeito de enganar. Por exemplo, vamos supor esta classe:

class Tranportador<T> where T : Mamifero {}

Ela faz o transporte de mamíferos. Ok, nada de mais. Se você tiver uma função que receba tipado assim:

void Tranportar(Tranportador<Mamifero> transportador) { }

Não consegue fazer isso:

var transportadorDeCachorro = new Tranportador<Cachorro>();

Tranportar(transportadorDeCachorro); //vai dar pau de compilação

Só isso:

var transportadorDeMamifero = new Tranportador<Mamifero>();

Tranportar(transportadorDeMamifero);

Deu para entender o drama?

E como resolve isso? Há uma maneira, mas você vai ter que alterar a função. Fica assim:

static void Tranportar<T>(Tranportador<T> transportador) where T : Mamifero { }

Essa função agora é uma função genérica. Antes a função não era genérica, ela tinha um argumento com o tipo bem definido: Tranportador<Mamifero> transportador. Na prática, a intenção da função anterior está mantida: faça o transporte de algum mamífero.

A chamada à função fica assim:

    1 var transportadorDeCachorro = new Tranportador<Cachorro>();

    2 Tranportar<Cachorro>(transportadorDeCachorro); //sem inferência de tipo

    3 Tranportar(transportadorDeCachorro); //com inferência de tipo

    4 var transportadorDeMamifero = new Tranportador<Mamifero>();

    5 Tranportar<Mamifero>(transportadorDeMamifero); //sem inferência de tipo

    6 Tranportar(transportadorDeMamifero); //com inferência de tipo

Notem que nem precisamos tipar a chamada. Ele já percebe, nas linhas 3 e 6, qual o tipo do método, pelo tipo do argumento.

Vocês já encontraram problemas parecidos? Como resolveram?

Tudo funciona quando você “pensa generics”. Há casos sem solução, mas na prática a lei da invariância é para ajudar.

E se você gostou do assunto, a Wikipedia fala um pouco mais (infelizmente em inglês, sem tradução para o português) e o Rick Byers fez um excelente post sobre o assunto, lá em 2005. Vale a pena dar uma olhada, principalmente no post do Rick. A Wikipedia pega um pouco mais pesado.


Postado na(s) categoria(s) .Net pelo Giovanni Bassi em 25 de setembro de 2008 às 19:03 | Tags: , , ,

Ninguém avaliou. Dê sua nota!

  • Currently 0/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5

Quem é Giovanni Bassi

Giovanni Bassi Sou uma pessoa apaixonada por tecnologia e especificamente por .Net. Sou consultor independente especialista em .Net, focado em arquitetura e melhores práticas. Tenho dezenas de artigos publicados na .Net Magazine, revista da qual sou editor técnico. Ministro palestras e cursos de vez em quando, e quando dá tempo eu respiro um pouco. Mais detalhes nesta página.

Calendário

«  janeiro 2009  »
seteququsedo
2930311234
567891011
12131415161718
19202122232425
2627282930311
2345678
Ver detalhamento de posts no calendário