Late binding vai muito bem, obrigado. Conceitos básicos de Reflection

No VB 6, que utiliza o padrão COM, usavamos com frequencia o conceito de Late Binding. O Late Binding nos permite utilizar um determinado componente em Run Time, sem que tenhamos previsto que componente seria este em design time.

Por exemplo, poderiamos guardar a identificação do componente em uma base de dados e instancia-lo em run-time através da instrução CreateObject.

Essa identificação do componente está diretamente ligada com o padrão COM. Trata-se do ProgID do componente, formado por nomes de duas partes que identificam o componente e a classe em si. Por exemplo, "ADODB.Connection" identifica a classe connection do componente ADODB.

Mas no .NET os componente que criamos não são COM e consequentemente não temos ProgID. Então como fazer o late binding neste ambiente ?

É ai que entra o conceito de Reflection. Reflection é a capacidade que temos no .NET de manipular classes em run-time, sem sabermos exatamente que classe estamos manipulando, podendo descobrir todas as características da classe em run-time.

Mas para isso precisamos identificar a classe de alguma forma. No COM tinhamos o progID, e no .NET ? No .NET, justamente por não termos o progid, o processo de Reflection é baseado no carregamento de um Assembly. A aplicação primeiramente carrega um assembly (.dll) para a memória, depois manipula seu conteúdo, criando instancias de suas classes e utilizando-as.

Um exemplo simples pode ser feito para demonstrar o uso de Reflections. Siga esses passos :

1) No visual Studio, crie uma solução com dois projetos, um Windows Form e uma Class Library

2) Defina o nome do projeto Class Library e o nome da classe. Irei utilizar clsLate e clHello, respectivamente, apenas como exemplo.

3) Mais uma vez, apenas por questão de exemplo, monte o seguinte código na classe :

Public Class clHello
Public Sub Hello()
MsgBox("Hello world from the class")
End Sub
End Class

4) Faça um rebuild do projeto da classe. Não precisa ser de tudo, apenas do projeto da classe.

5) Utilize o solution Explorer para copiar a dll clslate.dll do diretório bin do seu projeto original para o diretório bin da Windows Application. Observe que apesar de estarmos copiando a dll não estamos fazendo referencia a ela, o que, a principio, impediria o acesso a classe.

6) Insira um botão no formulário da Windows Application e chame-o de cmdHello

7) Monte o seguinte código :

Private Sub cmdHello_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles cmdHello.Click
Dim DLL As System.Reflection.Assembly
Dim obj As Object
DLL = System.Reflection.Assembly.Load("clsLate")
obj = DLL.CreateInstance("clsLate.clHello")
obj.hello()

End Sub

8) Teste a aplicação

Verá que apesar de não termos feito referência para o assembly, ele é carregado e a mensagem é exibida.

O que o código faz é carregar um assembly pelo nome ("clsLate") e a partir dele instanciar objetos contidos dentro deste assembly, tal como clsLate.clHello . É interessante como a organização NameSpace.Classe fica semelhante ao formato do ProgID, mas isso acontece apenas nos projetos mais simples.

Talvez você esteja agora se perguntando : "Mas eu vou ter que copiar o Assembly sempre que desejar fazer um acesso a ele ?".

Não necessariamente. Realmente, para uma aplicação acessar um determinado assembly um dos primeiros locais que é examinado em busca dele é o diretório bin abaixo da aplicação. Mas caso o assembly não seja encontrado neste local a aplicação irá procurar o assembly no Global Assembly Cache.

O GAC, como é popularmente chamado, é um cache de assemblys, permitindo que os assemblys possam ser acessados através de qualquer aplicação .NET na máquina (obedecendo as restrições de segurança, claro). Para controlarmos o conteúdo do GAC devemos estar utilizando uma aplicação chamada GACUTIL, que permite listar, inserir e remover assemblys do cache.

Veja algumas sintaxes :

GacUtil /l [<nome do assembly>] : lista os assemblys no cache
GacUtil /i [<nome da dll>] : Insere o assembly no cache
GacUtil /u [<nome do assembly>] : Remove o assembly do cache

Desta forma, não é preciso que a dll sempre seja copiada para junto da aplicação que a utilizará. Se o assembly estiver no Gac, a aplicação "client" irá localiza-lo sem problemas.

Ok. Com isso então vimos um uso simples do recurso de Reflection. Porém este recurso pode realmente fazer muito mais que isso.

Para demonstrar, vamos fazer um novo exemplo. Vamos criar uma aboutbox nesta windows application que começamos a desenvolver. Criem um novo Windows Form e deem o nome a ele de frmAbout.

Dentro deste formulário vamos criar o seguinte código :

Private Structure AssemblyAttributes
Public Name As String
Public Title As String
Public Description As String
Public Product As String
Public Version As String
Public Company As String
Public Copyright As String
Public Trademark As String
End Structure

Private Function GetAssemblyAttributes() As AssemblyAttributes
Dim Index As Integer
Dim sNameComponents() As String
Dim oCustAttrs As Object()
Dim oAssembly As [Assembly]
Dim aCompany As AssemblyCompanyAttribute
Dim aCopyright As AssemblyCopyrightAttribute
Dim aDescription As AssemblyDescriptionAttribute
Dim aProduct As AssemblyProductAttribute
Dim aTrademark As AssemblyTrademarkAttribute
Dim aTitle As AssemblyTitleAttribute
Try
'Load the main (startup) Assembly & retrieve version info from FullName property
oAssembly = [Assembly].GetEntryAssembly()
'FullName comes back in this format:
'"AboutTest, Version=1.0.641.37598, Culture=neutral, PublicKeyToken=null"
sNameComponents = oAssembly.FullName.Split(",") 'separate by commas
GetAssemblyAttributes.Name = sNameComponents(0)
GetAssemblyAttributes.Version = sNameComponents(1).Split("=")(1)
'Get Assembly attributes values
oCustAttrs = oAssembly.GetCustomAttributes(False) 'False = don't inherit
For Index = 0 To oCustAttrs.Length - 1
'Console.WriteLine("Type: {0}", oCustAttrs(Index).GetType.ToString)
Select Case oCustAttrs(Index).GetType.ToString
Case "System.Reflection.AssemblyCompanyAttribute"
aCompany = oCustAttrs(Index)
GetAssemblyAttributes.Company = aCompany.Company.ToString
Case "System.Reflection.AssemblyCopyrightAttribute"
aCopyright = oCustAttrs(Index)
GetAssemblyAttributes.Copyright = aCopyright.Copyright.ToString
Case "System.Reflection.AssemblyDescriptionAttribute"
aDescription = oCustAttrs(Index)
GetAssemblyAttributes.Description = aDescription.Description.ToString
Case "System.Reflection.AssemblyProductAttribute"
aProduct = oCustAttrs(Index)
GetAssemblyAttributes.Product = aProduct.Product.ToString
Case "System.Reflection.AssemblyTrademarkAttribute"
aTrademark = oCustAttrs(Index)
GetAssemblyAttributes.Trademark = aTrademark.Trademark.ToString
Case "System.Reflection.AssemblyTitleAttribute"
aTitle = oCustAttrs(Index)
GetAssemblyAttributes.Title = aTitle.Title.ToString
End Select
Next
Catch
'clear structure Name field if load fails
GetAssemblyAttributes.Name = ""
End Try
End Function

A função GetAssemblyAttributes preenche uma structure do tipo AssemblyAttributes com os atributos do Assembly atual. O Assembly em questão é obtido através do método GetEntryAssembly da classe Assembly. Observem que é um método estático, ou seja, a classe não precisou ser instanciada para que o método possa ser utilizado.

Para fazermos o preenchimento dos labels que esta aboutbox deverá possuir vamos utilizar a seguinte Sub :

Private Sub SetFieldDefaults()
Dim assemAttrib As AssemblyAttributes
'load attributes from the named assembly
assemAttrib = GetAssemblyAttributes()
If assemAttrib.Name.Length > 0 Then
If assemAttrib.Product.Length > 0 Then
TitleBarText = "About " & assemAttrib.Product
Else
TitleBarText = "About " & assemAttrib.Name
End If
If assemAttrib.Title.Length > 0 Then
ApplicationInfo = assemAttrib.Title
Else
ApplicationInfo = assemAttrib.Name
End If
If assemAttrib.Version.Length > 0 Then
VersionInfo = "Version " & assemAttrib.Version
Else
VersionInfo = ""
End If
CopyrightInfo = assemAttrib.Copyright
CustomInfo = ""
If assemAttrib.Company.Length > 0 Then
CustomInfo = "This application was developed by " & assemAttrib.Company & "."
If assemAttrib.Description.Length > 0 Then
CustomInfo = CustomInfo & ControlChars.CrLf & assemAttrib.Description
End If
End If
If assemAttrib.Trademark.Length > 0 Then
AdditionalInfo = "WARNING: " & assemAttrib.Trademark
Else
AdditionalInfo = ""
End If
End If
End Sub

Como podem observar, a sub SetFieldDefaults obtem um objeto AssemblyAttributes e o utiliza para preencher os labels existentes no formulário. Mas, observando em detalhes, você verá que nenhuma atribuição é feita diretamente para os labels. Este é realmente um excelente exemplo de encapsulamento onde a classe (formulário) fica encapsulada até dela mesma, tudo é acessado através de propriedades. Veja as propriedades :

 

'Allows user to get/set AboutBox Title bar text
Public Property TitleBarText() As String
Get
Return Me.Text
End Get
Set(ByVal Value As String)
Me.Text = Value.Trim
End Set
End Property

'Allows user to get/set lblApplicationInfo
Public Property ApplicationInfo() As String
Get
Return lblApplicationInfo.Text
End Get
Set(ByVal Value As String)
lblApplicationInfo.Text = Value.Trim
End Set
End Property

'Allows user to get/set lblVersionInfo
Public Property VersionInfo() As String
Get
Return lblVersionInfo.Text
End Get
Set(ByVal Value As String)
lblVersionInfo.Text = Value.Trim
End Set
End Property

'Allows user to get/set lblCopyrightInfo
Public Property CopyrightInfo() As String
Get
Return lblCopyrightInfo.Text
End Get
Set(ByVal Value As String)
lblCopyrightInfo.Text = Value.Trim
End Set
End Property

'Allows user to get/set lblCustomInfo
Public Property CustomInfo() As String
Get
Return lblCustomInfo.Text
End Get
Set(ByVal Value As String)
lblCustomInfo.Text = Value.Trim
End Set
End Property

Por fim, vamos configurar as características da nossa aplicação dentro do arquivo AssemblyInfo.vb . Veja como ficam as tags ;

<Assembly: AssemblyTitle("AboutBase")>
<Assembly: AssemblyDescription("AboutBox Base Class")>
<Assembly: AssemblyCompany("BufaloInfo")>
<Assembly: AssemblyProduct("Exemplo de About Box")>
<Assembly: AssemblyCopyright("Copyright © 2002 by Bufalo")>
<Assembly: AssemblyTrademark("")>

Pronto. Com isso temos uma aboutbox que estará sempre automaticamente preenchida de acordo com as informações do Assembly que tenham sido inseridas no AssemblyInfo.vb

Temos então uma demonstração de até onde a Reflection pode nos levar. E isso é apenas o inicio, usando o recurso de Reflection todo o conteúdo de um assembly pode ser analisado e manipulado em run-time.

Dennes Torres

Dennes Torres possui as certificações MCAD,MCSD,MCSE e MCDBA , é diretor da Búfalo Informática (www.bufaloinfo.com.br) , empresa de treinamento do Rio de Janeiro e é líder do grupo de usuários DevASPNet (www.devaspnet.com.br) , grupo de usuários .NET do Rio de Janeiro.