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:
- C# 4.0 – Quanto antes melhor – onde apresentei um resumo das novidades, além de assuntos que envolvem a nova versão da linguagem;
- 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.
- C# 4.0: Teremos covariância e contravariância – onde apresentei a tão esperada variância do C#.
- 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):
- 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.)
- Todos os métodos retornam dynamic, e não object.
- 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":
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):
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!
