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!

Giovanni Bassi

Arquiteto e desenvolvedor, agilista, escalador, provocador. É fundador e CSA da Lambda3. Programa porque gosta. Acredita que pessoas autogerenciadas funcionam melhor e por acreditar que heterarquia é mais eficiente que hierarquia. Foi reconhecido Microsoft MVP há mais de dez anos, dos mais de vinte que atua no mercado. Já palestrou sobre .NET, Rust, microsserviços, JavaScript, TypeScript, Ruby, Node.js, Frontend e Backend, Agile, etc, no Brasil, e no exterior. Liderou grupos de usuários em assuntos como arquitetura de software, Docker, e .NET.