<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" media="screen" href="/~d/styles/rss2portuguesefull.xsl"?><?xml-stylesheet type="text/css" media="screen" href="http://feeds.feedburner.com/~d/styles/itemcontent.css"?><rss xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/" xmlns:feedburner="http://rssnamespace.org/feedburner/ext/1.0" version="2.0"><channel><title>Ankaa.net</title><link>http://www.ankaa.net</link><description>RSS feeds for Ankaa.net</description><ttl>60</ttl><atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="self" type="application/rss+xml" href="http://feeds.feedburner.com/AnkaanetArtigos" /><feedburner:info uri="ankaanetartigos" /><atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="hub" href="http://pubsubhubbub.appspot.com/" /><feedburner:feedFlare href="http://add.my.yahoo.com/rss?url=http%3A%2F%2Ffeeds.feedburner.com%2FAnkaanetArtigos" src="http://us.i1.yimg.com/us.yimg.com/i/us/my/addtomyyahoo4.gif">Subscribe with My Yahoo!</feedburner:feedFlare><feedburner:feedFlare href="http://www.newsgator.com/ngs/subscriber/subext.aspx?url=http%3A%2F%2Ffeeds.feedburner.com%2FAnkaanetArtigos" src="http://www.newsgator.com/images/ngsub1.gif">Subscribe with NewsGator</feedburner:feedFlare><feedburner:feedFlare href="http://feeds.my.aol.com/add.jsp?url=http%3A%2F%2Ffeeds.feedburner.com%2FAnkaanetArtigos" src="http://o.aolcdn.com/favorites.my.aol.com/webmaster/ffclient/webroot/locale/en-US/images/myAOLButtonSmall.gif">Subscribe with My AOL</feedburner:feedFlare><feedburner:feedFlare href="http://www.bloglines.com/sub/http://feeds.feedburner.com/AnkaanetArtigos" src="http://www.bloglines.com/images/sub_modern11.gif">Subscribe with Bloglines</feedburner:feedFlare><feedburner:feedFlare href="http://www.netvibes.com/subscribe.php?url=http%3A%2F%2Ffeeds.feedburner.com%2FAnkaanetArtigos" src="http://www.netvibes.com/img/add2netvibes.gif">Subscribe with Netvibes</feedburner:feedFlare><feedburner:feedFlare href="http://fusion.google.com/add?feedurl=http%3A%2F%2Ffeeds.feedburner.com%2FAnkaanetArtigos" src="http://buttons.googlesyndication.com/fusion/add.gif">Subscribe with Google</feedburner:feedFlare><feedburner:feedFlare href="http://www.pageflakes.com/subscribe.aspx?url=http%3A%2F%2Ffeeds.feedburner.com%2FAnkaanetArtigos" src="http://www.pageflakes.com/ImageFile.ashx?instanceId=Static_4&amp;fileName=ATP_blu_91x17.gif">Subscribe with Pageflakes</feedburner:feedFlare><item><comments>http://www.ankaa.net/Artigos/tabid/114/articleType/ArticleView/articleId/23/Serializaccedilatildeo.aspx#Comments</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.ankaa.net/DesktopModules/DnnForge%20-%20NewsArticles/RssComments.aspx?TabID=114&amp;ModuleID=491&amp;ArticleID=23</wfw:commentRss><trackback:ping>http://www.ankaa.net/DesktopModules/DnnForge%20-%20NewsArticles/Tracking/Trackback.aspx?ArticleID=23&amp;PortalID=0&amp;TabID=114</trackback:ping><title>Serializa&amp;ccedil;&amp;atilde;o</title><link>http://feedproxy.google.com/~r/AnkaanetArtigos/~3/UY89bdJxstQ/Serializaccedilatildeo.aspx</link><description>Introdução    "Serialization" consiste em guardar um objecto num elemento de armazenamento (um ficheiro, uma base de dados, um "buffer de memória, etc.), permitindo a sua posterior reutilização, através de um processo de "deserialization", voltando a recriar o objecto na sua forma original.      A Serialização é um processo chave da plataforma .Net sendo utilizado de forma transparente para funcionalidades tais como por exemplo para enviar um objecto por valor para outra aplicação.      Um objecto deve ser tornado serializável se se pretender envia-lo para outra aplicação ou grava-lo em disco ou base de dados (Por exemplo para guardar um objecto num objecto de sessão, na camada de apresentação de uma aplicação "web").      O conceito de persistência é complementar ao conceito de serialização. Ambos consistem no armazenamento de um objecto, mas o conceito de persistência define que o meio utilizado é persistente (por exemplo um ficheiro) enquanto o conceito de serialização é independente da persistência do meio (por exemplo, na memória RAM).   Conceitos básicos   A "framework" .Net sabe como serializar todos os tipos básicos de dados, incluindo "integers", "strings" e "arrays" numéricos e de "strings", por isso é possível manter estes objectos serializados em objectos de "stream" (ficheiros por exemplo) com um esforço mínimo. O único requisito para esta operação é a criação de um objecto "formatter" adequado.   Um objecto "formatter" é um objecto que implementa o interface "IFormater" definido no namespace "System.Runtime.Serialization.Namespace". É possível ao programador criar um objecto de "formatter" especifico, criando uma classe que implementa este interface. No entanto o mais frequente é utilizar-se um dos objectos que já existem na plataforma .Net.   "Formatter Objects"   A framework disponibiliza os seguintes objectos:         "BinaryFormatter": Definido no namespace "System.RunTime.Serialization.Formatters.Binary" disponibiliza um mecanismo eficiente para guardar objectos num formato binário compacto. Na verdade é a própria representação binária do objecto em memória que é mantida, por isso os processos de "serialization" e "deserialization" são muito rápidos.        "SoapFormatter": definido no namespace "System.RunTime.Serialization.Formatters.Soap", guarda os objectos num formato reconhecível por humanos, em XML, seguindo as especificações SOAP ("Simple Object Access Protocol"). Os processos de serialização de deserialização são relativamente mais lentos que através do "BinaryFormatter", no entanto tem a vantagem de permitir o envio de dados entre aplicações através de HTTP, bem como de permitir a visualização dos dados numa forma compreensível.    "Binary Serialization"   Os métodos chave que todos os objectos "Formatter" suportam são "Serialize" e "Deserialize"   O método "Serialize" requer como argumentos um objecto do tipo "Stream" e o objecto a ser serializado.   Para recuperar o objecto, é utilizado o método "Deserialize" que aceita como único argumento o objecto de "Stream" e retorna o objecto original. Este objecto é devolvido no tipo "Object" por isso tem de ser convertido no tipo original.      'Passo 1 - Guardar num ficheiro 
'Cria um array de inteiros 
Dim Arr() As Integer = {1, 2, 4, 8, 16, 32, 64, 128, 256} 
'Cria um Stream para guardar o objecto 
Using fs As New FileStream("D:\myfile.dat", FileMode.Create) 
    'Cria o "Formatter" binário 
    Dim MyBinaryFormatter As New BinaryFormatter 
    'Copia o objecto para o ficheiro 
    MyBinaryFormatter.Serialize(fs, Arr) 
End Using 

'Passo 2 - Repõe o objecto 
Dim ArrCopia() As Integer 
'le o ficheiro com o objecto 
Using fs2 As New FileStream("D:\myfile.dat", FileMode.Open) 
    Dim MyBinaryFormatter2 As New BinaryFormatter 
    'Deseriliza o conteudo do ficheiro 
    ArrCopia = DirectCast(MyBinaryFormatter2.Deserialize(fs2), Integer()) 
End Using


Exemplo prático: Conjugando o estudo já efectuado sobre genéricos com a serialização, é possível construir um mecanismo genérico para serialização e deserialização de objectos: 


  Public Sub SerializeToFile(Of T)(ByVal path As String, ByVal obj As T) 
    ' Cria um Stream para guardar o objecto 
    Using fs As New FileStream(path, FileMode.Create) 
        ' Cria o "Formatter" binário 
        Dim bf As New BinaryFormatter() 
        ' Copia o objecto para o ficheiro 
        bf.Serialize(fs, obj) 
    End Using 
End Sub 

Public Function DeserializeFromFile(Of T)(ByVal path As String) As T 
    ' le o ficheiro com o objecto 
    Using fs As New FileStream(path, FileMode.Open) 
        Dim bf As New BinaryFormatter() 
        ' Deseriliza o conteudo do ficheiro 
        Return DirectCast(bf.Deserialize(fs), T) 
    End Using 
End Function


Demo: 

 Download Demo

"SOAP Serialization" 

O "SoapFormatter" esta marcado como obsoleto na "framework" 2.0. Segundo a "Microsoft" deve optar-se sempre que possível pelo "Binary Formatter". Contudo o "Soap Formatter" possui uma capacidade que falta ao "Binary Formatter", a capacidade de serializar os objectos em XML, legível por humanos. 

Esta capacidade é muito útil, especialmente durante o tempo de desenvolvimento e "debug". É por isso frequente a utilização deste "formatter" durante o desenvolvimento e passagem para o "Binary Formatter" em produção. 

O namespace "System.RunTime.Serialization.Formatters.Soap" pertence ao "assembly" "System.RunTime.Serialization.Formatters.Soap.dll". Este "assembly" não é referenciado automaticamente pelo "visual studio", pelo que é necessário criar a referencia manualmente. 


  'Cria um hashtable e coloca-lhe alguns dados 
Dim MyHash As New Hashtable 
MyHash.Add("one", 1) 
MyHash.Add("Two", 2) 
MyHash.Add("Three", 3) 
'Cria um SoapSerializer 
Dim MySoapFormatter As New SoapFormatter 
'Guarda a hashtable em disco no formato SOAP 
Using fs As New FileStream("D:\MyHash.xml", FileMode.Create) 
    MySoapFormatter.Serialize(fs, MyHash) 
End Using 
'Recria o objecto utilizando o mesmo SoapFormatter 
Dim MyHashCopy As Hashtable 
Using fs As New FileStream("D:\MyHash.xml", FileMode.Open) 
    MyHashCopy = DirectCast(MySoapFormatter.Deserialize(fs), Hashtable) 
End Using


Demo: 

 Download Demo

Criação de tipos serializáveis 

Para além da serialização de tipos básicos da plataforma, é possível ao programador criar classes serializáveis. 

Para criar uma classe serializável apenas é necessário utilizar um atributo especial: "Serializable": 


   _ 
Class Person 
    '… 
End Class


Para que a serialização funcione são necessárias duas condições:    


  A classe base, caso exista tem de serializável    

  Todos os campos da classe tem de ser de tipos serializáveis.    


Atributo "NonSerialized" 

O atributo "NonSerialized" indica que um determinado campo de uma classe "serializable" não deve ser serializado. Este atributo deve ser utilizado nos seguintes casos: 

Quando uma classe possui um campo de um tipo não serializável (por exemplo o system.windows.form.control), este atributo é obrigatório para que a classe seja serializada. 

Este atributo também é obrigatório quando o valor de um campo deixa de ser válido após deserialização (por exemplo apontadores, handles para ficheiros, etc.) 

Quando o valor de um campo é facilmente calculável a partir de outros campos, este atributo pode ser utilizado para diminuir a quantidade de informação a serializar. 

 Private Xpto As Xpto 

"Object Graphs" 

Um "object graph" é um conjunto de múltiplos objectos com referências entre si. 

É possível serializar um "object graph", mesmo que possua referencias circulares. 


  Dim p1 As New Person("Antonio", "Silva", #1/12/1960#) 
Dim p2 As New Person("João", "Ferreira", #3/6/1962#) 
Dim p3 As New Person("Ana", "Dias", #10/4/1965#) 
'Estabelecer uma referência circular entre duas pessoas 
p2.Spouse = p3 
p3.Spouse = p2 
'Colocar todas as pessoas numa lista de pessoas 
Dim List As New List(Of Person)(New Person() {p1, p2, p3}) 
'Serializa para o disco através dos métodos genéricos do exemplo anterior 
Dim MyGenericSerialization As New GenericSerialization 
MyGenericSerialization.SerializeToFile("D:\Persons.Dat", List) 
'Cria uma segunda lista e obtem os dados 
Dim list2 As List(Of Person) = MyGenericSerialization.DeserializeFromFile(Of List(Of Person))("D:\Persons.Dat")


"Custom Serialization" 

Quando é necessária uma solução mais complexa para lidar com questões de "serialization" e "deserialization" torna-se necessário desenvolver uma "Custom Serialization". 

Uma "Custom Serialization" pode ser útil em casos como:    


  Quando é necessário decidir dinamicamente que informação deve ser serializada e mantida.    

  Quando é necessário executar código quando o objecto é deserializado, provavelmente para recalcular valores que entretanto deixaram de ser válidos.    


Interface IDeserializationCallBack 

O caso mais simples de "Custom Serialization" consiste em executar algum código quando o objecto é completamente deserializado. 

O exemplo seguinte contempla o seguinte cenário:   


  Uma classe "person" abre um ficheiro no construtor, e todos os outros métodos da classe dependem deste ficheiro.    

  Quando o objecto é deserializado, o construtor não é executado novamente, como tal se nada for feito, a aplicação irá falhar. A solução é implementar nesta classe o interface "IDeserializationCallBack".    

  Este interface possui um único método "OnDeserialization" o qual é invocado pela "framework" quando o objecto é totalmente deserializado.    

  É importante notar que a framework invoca o método "OnDeserialization" quando todo o "object graph" estiver sido deserializado, o que significa que garantidamente, todos os objecto dependentes do objecto corrente foram iniciados correctamente. Esta informação é importante se os campos a iniciar no método "OnDeserialization" dependerem de outros campos.    



    


   _ 
Public Class Person 
    Implements IDeserializationCallback 
    Implements IDisposable 
     Private LogStream As FileStream 
    Public FirstName As String 
    Public LastName As String 
    Private Sub OpenLogFile() 
        Dim fileName As String = Me.FirstName &amp; " " &amp; Me.LastName &amp; ".txt" 
        LogStream = New FileStream(fileName, FileMode.OpenOrCreate) 
    End Sub 

    Sub New(ByVal firstName As String, ByVal lastName As String) 
        Me.FirstName = firstName 
        Me.LastName = lastName 
        ' Abre o ficheiro para logging. 
        OpenLogFile() 
    End Sub 
    ' Este método é chamado quando o objecto foi completamente deserialized. 
    Private Sub OnDeserialization(ByVal sender As Object) Implements IDeserializationCallback.OnDeserialization 
        ' ReAbre o ficheiro 
        OpenLogFile() 
    End Sub 
    Public Sub Dispose() Implements IDisposable.Dispose 
        If LogStream IsNot Nothing Then LogStream.Close() 
    End Sub 
End Class


Interface Iserializable 

O interface "ISerializable" permite um controlo total sobre ambos os processos de serialização e deserialização. 

Este interface útil em casos em que se pretende definir em tempo de execução quais os campos a serializar ou quando se pretende serializar campos num formato diferente do padrão. 

O Interface "ISerializable" expõe apenas um método "GetObjectData" e tem a seguinte sintaxe: 


  Protected Sub GetObjectData(ByVal info As SerializationInfo, ByVal Context As StreamingContext) 
    '… 
End Sub


O método "GetObjectData" é invocado quando o objecto é passado para o método "Serialize" do "formatter object". 

O seu propósito é preencher o objecto "SerializationInfo" com toda a informação do objecto a ser serializado. 

O código dentro deste método pode examinar a estrutura "StreamingContext" para obter detalhes acerca do processo de serialização (por exemplo para determinar se o objecto está a ser serializado para um ficheiro ou para memória). 

A presença do interface "ISerializable" implica a existência de um construtor especial com a estrutura: 


  Protected Sub New(ByVal info As SerializationInfo, ByVal Context As StreamingContext) 
    '… 
End Sub


A "framework" chama este construtor quando o objecto é deserializado. A não inclusão deste construtor não gera nenhum erro de compilação, no entanto será gerado um erro de "run time" quando o objecto for deserializado. 

O "Scope" utilizado para o método "GetObjectData" e para o construtor é crucial e tem de ser:    


  "Protected": Se a classe pode ser base para outras classe derivadas 

  "Private": Se a classe for "Sealed".    


O scope nunca deve ser "Public", pois sendo "Protected" ou "Private" o tipo tem obrigatóriamente de possuir pelo menos mais um construtor público, para que o tipo possa ser instanciado. 


  Nota: O objecto "StreamingContext" é um objecto especial que permite associar o objectivo da serialização ao "formatter object" seleccionado. Este objecto é aceite como segundo argumento num construtor "overloaded". 


A utilização deste objecto é recomendada e é considerada uma boa prática, pois permite tomar decisões de acordo com o tipo de serialização. Exemplo: 


  Dim SC As New StreamingContext(StreamingContextStates.file) 
Dim BF As New BinaryFormatter(Nothing, SC)


 

O argumento do método "GetObjectData" constituído pelo objecto "SerializeInfo" funciona como um dicionário que é preenchido com os valores a serem serializados, através do método "AddValue": 


  Protected Sub GetObjectData(ByVal info As SerializationInfo, ByVal Context As StreamingContext) Implements ISerializable.GetObjectData 
    'Guarda os campos 
    info.AddValue("FirstName", Me.FirstName) 
    info.AddValue("Lastname", Me.LastName) 
    ' … 
End Sub


Como esperado, o objecto passado para o método "AddValue" tem de ser "serializable" 

Os valores são obtidos posteriormente através do método "GetValue": 


  Protected Sub New(ByVal info As SerializationInfo, ByVal Context As StreamingContext) 
    'Obtem os campos 
    Me.FirstName = CStr(info.GetValue("Firstname", GetType(String))) 
    Me.LastName = info.GetString("LastNAme") 
    ' … 
End Sub&lt;img src="http://feeds.feedburner.com/~r/AnkaanetArtigos/~4/UY89bdJxstQ" height="1" width="1"/&gt;</description><dc:creator>Migasp</dc:creator><pubDate>Fri, 20 Jun 2008 19:14:46 GMT</pubDate><guid isPermaLink="false">f1397696-738c-4295-afcd-943feb885714:23</guid><feedburner:origLink>http://www.ankaa.net/Artigos/tabid/114/articleType/ArticleView/articleId/23/Serializaccedilatildeo.aspx</feedburner:origLink></item><item><comments>http://www.ankaa.net/Artigos/tabid/114/articleType/ArticleView/articleId/22/Assemblies.aspx#Comments</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.ankaa.net/DesktopModules/DnnForge%20-%20NewsArticles/RssComments.aspx?TabID=114&amp;ModuleID=491&amp;ArticleID=22</wfw:commentRss><trackback:ping>http://www.ankaa.net/DesktopModules/DnnForge%20-%20NewsArticles/Tracking/Trackback.aspx?ArticleID=22&amp;PortalID=0&amp;TabID=114</trackback:ping><title>Assemblies</title><link>http://feedproxy.google.com/~r/AnkaanetArtigos/~3/aG6a7sHJJk8/Assemblies.aspx</link><description>Introdução   Um "Assembly" é uma colecção de módulos executáveis (.exe, .dll) e não executáveis (imagens, html, etc.), correspondendo à unidade mínima de código para reutilização, versionamento, e "Deployment" em aplicações .Net.   A "framework" .Net suporta dois tipos de "Assemblies":         "Private"        "Shared" ou "Strong named"     "Private assemblies"   Um "Assembly" privado é mantido dentro da pasta de uma aplicação e utilizado exclusivamente por essa aplicação, ou por outra aplicação, desde que instalada no mesmo directório.   Este tipo de "Assembly" é normalmente mais simples de construir e administrar, permitindo o "XCOPY Deployment".   Os "Private Assemblies" resolvem o problema típico do "DLL Hell" uma vez que cada aplicação possui a sua própria cópia da DLL. A desvantagem é que se for um componente partilhado por múltiplas aplicações, irão existir várias cópias do mesmo ficheiro.   "Shared assemblies"   Um "Shared Assembly" é instalado numa localização especifica do sistema, normalmente no directório "\Windows\Assembly". Este directório é conhecido por GAC – "Global Assembly Cache".   A "framework" .Net instala uma "shell extension" especial que permite explorar o GAC através do "Windows Explorer".   A inclusão ou remoção de "assemblies" no GAC apenas é possível com privilégios de administração.   A inclusão de "Assemblies" é feita utilizando o utilitário "GACUTIL", enquanto a remoção é feita através da opção "UnInstall" da "Shell extension" do "windows explorer".   Diferenças entre "Private Assemblies" e "Shared Assemblies"         Gestão de versões: Um "Shared Assembly" suporta informação sobre a versão, e é possível manter no GAC diferentes versões do mesmo "assembly" sem que se verifique qualquer conflito. Cada aplicação continua a funcionar com a versão com que foi compilada. Os "Assemblies" privados também podem possuir informação de versão, mas esta informação não é utilizada pela "framework". Serve apenas como referência para o desenvolvimento.        Robustez: Um "Shared Assembly" registado no GAC torna a aplicação mais robusta por duas razões:             Quando um "Assembly" é adicionado ao GAC é feito um teste de integridade para garantir que não foi alterado desde a sua compilação.         Apenas um utilizador com privilégios de administração pode adicionar ou remover elementos ao GAC.                Eficiência: Um "Shared Assembly" registado no GAC é mais eficiente pois o "Run Time" consegue encontra-lo mais rapidamente e não necessita de fazer uma verificação de disponibilidade. Em alguns casos, se várias aplicações utilizarem o mesmo "Assembly", o "Run Time" consegue utilizar apenas uma versão instaciada em memória.        "Side-by-side execution": Duas ou mais versões de um "Shared Assembly" podem ser executadas no mesmo processo. Por exemplo uma aplicação pode utilizar a versão 1.0 de um "assembly" e um outro "assembly" que por sua vez utiliza a versão 1.1 do primeiro "assembly".        Segurança: Um "Shared Assembly" pode ser assinado digitalmente, garantindo a origem do seu programador bem como que o seu código não foi alterado.          Nota: A codificação de um "Private assembly" ou de um "Shared assembly" é feita da mesma forma, uma vez que a única diferença entre os dois tipos é apenas a definição de um atributo.Contudo, uma vez que o principal objectivo de um "shared assembly" é ser partilhado por várias aplicações, é necessário ter atenção aos recursos utilizados. Por exemplo se um "shared assembly" utiliza ficheiros numa localização fixa, outras chamadas de outros clientes podem alterar esses ficheiros.    "Assemblies" e "Strong Names"        No caso dos "private assemblies", que existem apenas junto da aplicação, a probabilidade de existência de diferentes "assemblies" com o mesmo nome é pequena e a responsabilidade da sua gestão e da exclusiva responsabilidade do programador.        No caso dos "shared assemblies", normalmente partilhados no GAC é vital que a "Run Time" do .Net consiga distinguir dois "assemblies" com o mesmo nome mas de autores diferentes.        A "framework" .Net garante que o nome de um "shared assembly" é único ao nível do sistema, assignando um "Strong Name".        Um "Strong Name" resulta da combinação do seu nome, versão, cultura, de uma chave pública e de um valor que representa a arquitectura do processador.        Representada de uma forma textual, um "strong name" tem um aspecto semelhante ao seguinte:    Mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089, ProcessorArchitecture=x86    A geração de um "shared assembly" com "strong name" é feita em dois passos:        É executado o utilitário de linha de comando "SN" que irá criar um ficheiro ".snk" que contém um par de chaves aleatórias (uma publica e uma privada). Este utilitário, que existe no directório "\Program Files\Microsoft Visual Studio 8\SDK\v2.0\Bin" deve executado com a opção –k, da seguinte forma:          Sn –k MyKey.snk          Este ficheiro deve ser bem guardado e utilizado para assinar digitalmente todos os "assemblies" de um autor ou de uma companhia.        É adicionado um atributo de "assembly" que indica ao compilador que deve utilizar a chave pública contida no ficheiro .snk na compilação, introduzindo a linha seguinte no ficheiro "AssemblyInfo.vb":                   Instalação de "assemblies" no GAC   É possível instalar um "assembly" no GAC de duas formas:      "Drag-andDrop": Este método é extremamente simples. Basta utilizar o windows explorer e navegar até à pasta "\Windows\Assembly" e arrastar o ficheiro ".dll".        Utilitário "GacUtil": Este método é também simples, tendo a vantagem de poder ser incluido num "script" de instalação.                 Gacutil /i testassembly.dll        Uma vez que um dos objectivos do GAC é permitir a coabitação de diferentes versões do mesmo "assembly", a instalação não remove qualquer versão antiga. O programador ou o administrador da máquina deve ser responsável por manter no GAC apenas as versões necessárias, removendo as mais antigas. É também possível utilizar o comando gacutil com o parâmetro /u para remover as versões antigas antes de instalar uma nova.   O facto de adicionar um "assembly" ao GAC não o torna visível no "Add Reference" do "visual studio".   Para que o "assembly" apareça no "Add References" tornando a sua reutilização mais simples, é necessário proceder a um dos dois passos seguintes:         Copiar o ficheiro ".dll" para o directório "\Program Files\Microsft Visual Studio 8\Common7\IDE\PublicAssemblies.       Registar uma chave de "Registry" chamada por exemplo "MyAssemblies" dentro da pasta "HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\.NETFramework\AssemblyFolders, e colocar como valor "deafult" o endereço para um caminho de disco que corresponda a um directório onde existem os nossos "assemblies".&lt;img src="http://feeds.feedburner.com/~r/AnkaanetArtigos/~4/aG6a7sHJJk8" height="1" width="1"/&gt;</description><dc:creator>Migasp</dc:creator><pubDate>Fri, 20 Jun 2008 18:59:51 GMT</pubDate><guid isPermaLink="false">f1397696-738c-4295-afcd-943feb885714:22</guid><feedburner:origLink>http://www.ankaa.net/Artigos/tabid/114/articleType/ArticleView/articleId/22/Assemblies.aspx</feedburner:origLink></item><item><comments>http://www.ankaa.net/Artigos/tabid/114/articleType/ArticleView/articleId/21/Interfaces.aspx#Comments</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.ankaa.net/DesktopModules/DnnForge%20-%20NewsArticles/RssComments.aspx?TabID=114&amp;ModuleID=491&amp;ArticleID=21</wfw:commentRss><trackback:ping>http://www.ankaa.net/DesktopModules/DnnForge%20-%20NewsArticles/Tracking/Trackback.aspx?ArticleID=21&amp;PortalID=0&amp;TabID=114</trackback:ping><title>Interfaces</title><link>http://feedproxy.google.com/~r/AnkaanetArtigos/~3/HFzOTdKeekM/Interfaces.aspx</link><description>Introdução      A Framework .Net define vários interfaces importantes, pelo que é necessário entender como se pode aproveitar o seu potencial, quer seja pela invocação dos seus métodos ou através da sua implementação em tipos próprios.      Muitas vezes é possível definir a estrutura de uma aplicação através da implementação de interfaces.      Os interfaces, em conjunto com conceitos como herança e atributos são um dos pilares para o desenho de aplicações .Net   Definição e implementação   Em traços gerais, um interface é o conjunto de membros que uma classe expõe.   Por exemplo, todos os membros públicos de uma classe pertencem ao interface principal da classe.   Uma classe pode no entanto expor outros grupos de propriedades e métodos que não tem visibilidade pública.   Interface … End Interface   Um interface define apenas a assinatura de propriedades e métodos, sendo a sua codificação da responsabilidade da classe que o implementa.   Diferentes classes podem implementar de forma diferente um mesmo interface (Polimorfismo).      Interface IAddin 
    ReadOnly Property Id() As Long 
    Property State() As Boolean 
    Function OnConnection(ByVal Environment As String) As Boolean 
    Sub OnDisconnection() 
End Interface


Regras para a criação de interfaces    


  Os interfaces não podem incluir código, apenas assinaturas de propriedades e métodos.  

  É possível definir as assinaturas de propriedades como "ReadOnly" e "WriteOnly", mas não é possível especificar valores específicos para os blocos "Set" ou "Get".  

  Um interface não pode incluir variáveis, nem pode definir "Scope qualifiers", pois todos os membros são implicitamente públicos. 
      

  Um interface pode incluir eventos públicos, mas a sua utilização é rara e não é recomendada. 
      

  Por "default" o "Scope qualifier" de um interface é "Friend".   


Por convenção os nomes dos interfaces começam pelo caracter "I", não incluem o caracter "_" e utilizam a notação Pascal (ex: "IGetRange")    

Implementação 

Uma classe implementa um interface através da utilização da expressão "Implements": 


  Class MyAddin 
    Implements IAddin 
    '… 
End Class


Ao implementar um interface, o "visual studio" cria automaticamente um template da implementação. 

A sintaxe para implementação das propriedades e métodos individuais reutiliza a expressão "Implements" para especificar exactamente qual o membro que esta a ser implementado. 

O código do template gerado automaticamente pelo "Visual Studio" utiliza o "Scope Qualifier" "Public" para todos os membros, para que estes membros possam ser invocados directamente pelos seus clientes, mas este comportamento pode ser alterado. Ex: 


  Class MyAddin 
    Implements IAddin 
    Protected Overridable Function OnConnect(ByVal environment As String) As Boolean Implements IAddin.OnConnection 
        '… 
    End Function 
End Class


A expressão "Implements" suporta múltiplos argumentos, pelo que é possível um único método implementar membros de vários interfaces: (Mesmo que os membros tenham nomes diferentes, embora com a mesma assinatura): 


  'Outro interface com apenas uma propriedade 
Interface IHostEnvironment 
    ReadOnly Property HashCode() As Long 
End Interface 

'Classe de implementação 
Class MyAddin 
    Implements IAddin, IHostEnvironment 
    Public ReadOnly Property Id() As Long Implements IAddin.Id, IHostEnvironment.HashCode 
        Get 
            '… 
        End Get 
    End Property 
End Class


Acesso 

É possível aceder aos membros de um interface de duas formas:    


  Directamente – Através de uma variável da classe. Neste caso o acesso ao membro é feito tal como se se tratasse de um membro regular    

  Através de uma variável do interface. Neste caso o acesso ao membro é feito através da instanciação de uma variável do tipo do interface que a classe implementa. 



  'Uma instancia da classe 
Dim addin As New MyAddin 
'Cast para uma variável de interface 
Dim iadd As IAddin = addin 
'Agora a variavel de interface tem acesso a todos os membros do interface 
Iadd.state=True


Através de uma variável de interface é possível aceder a todos os membros independentemente do "Scope Qualifier" definido na classe que o implementa (Por exemplo, é possível invocar um método privado, desde que este pertença à implementação do interface). 

Se o membro do interface for definido como "Public" na classe que o implementa, não há qualquer necessidade de utilizar uma variável do tipo do intreface. 

Exemplo: 


  'Código do Interface 
Public Interface IDemo 
    Function GetTime() As String 
End Interface 

'Código da classe de implementação 
Public Class Class1 
    Implements IDemo 
    Private Function GetTime() As String Implements IDemo.GetTime 
        Return Now 
    End Function 
End Class 

'Código do Cliente 
Module Module1 
    Sub Main() 
        Dim MyClass1 As New Class1 
        'A linha seguinte não é aceite 
        Dim MyClass1.GetTime 
        Dim MyIdemo As IDemo 
        MyIdemo = CType(MyClass1, IDemo) 
        'Agora já é possivel aceder ao membro privado 
        MsgBox(MyIdemo.GetTime) 
        'Alternativa, sem ser necessário criar variável explicita: 
        'MsgBox(CType(MyClass1, IDemo).GetTime()) 
    End Sub 
End Module


Herança 

Um interface pode herdar de outro interface. Um interface derivado contem todos os membros do interface base, bem como todos os membros por si definidos. Esta característica permite criar um novo interface que é uma extensão de outro. 


  Public Interface IAddin2 
Inherits IAddin 
    Property Description() As String 
End Interface


A Framework .net dispobiliza vários interfaces derivados. Por exemplo o "ICollection" deriva do "IEnumerable" e é base para os "IList" e "IDictionary". 

Regras para a herança de Interfaces   


  Um Interface derivado não pode redefinir os membros do interface base (as expressões "Overridable" e "Overrides" não são aceites na definição de interfaces.    

  Se um interface derivado possuir um membro com o mesmo nome que um membro no interface base, o membro do interface derivado esconde ("Shadows") o membro do interface base. (Neste caso ocorre uma situação bastante confusa: A classe que implementa o interface derivado é obrigada a codificar ambos os membros com o mesmo nome, por isso esta situação deve sempre ser evitada).    

  Uma classe derivada herda automaticamente todos os interfaces (e a sua implementação) tal como definidos na classe base. Esta classe derivada não pode conter a expressão "Implements".    

  A classe derivada pode redefinir a implementação de um método definido num interface, desde que não tenha sido definido como privado, e tenha a expressão "Overridable"    

  
    Class Class2 
    Inherits MyAddin 
    Protected Overrides Function OnConnection(ByVal environment As String) As Boolean 
        '… 
    End Function 
End Class 
  



  
  Interfaces da Framework 

A Framework .Net define vários interfaces, que podem ser implementados nas classes criadas pelos programadores. 

Icomparable 

O Tipo "System.Array" expõe um método estático chamado "Sort", que permite ordenar um "array" de tipos simples, como "integers" ou "strings", mas não permite ordenar objectos mais complexos pois não esta definida a forma como é feita a comparação. 

Demo: 

 Download Demo

Com a implementação do interface "IComparable", é possível criar uma classe que seja ordenável através deste método "Sort". Este interface expõe apenas um método chamado "CompareTo", que recebe um objecto e é esperado que retorne -1, 0 ou 1 dependendo de se o objecto em que o "CompareTo" foi chamado é menor, igual ou maior que o objecto passado no argumento. 

Demo: 

 Download Demo

ICloneable 

Como no .Net todos os tipos derivam do tipo "Object", quando uma variável (de um tipo referência) é definida como sendo igual a outra variável, obtém-se duas variáveis a apontar para o mesmo objecto, em vez de dois objectos independentes. 

Tipicamente, é possível obter uma cópia do objecto invocando um método especial exposto pela classe, chamado "Clone". 

Para que a classe exponha este método especial, tem de implementar o interface "ICloneable"e o seu único método "Clone". 

Ao contrário do "IComparable", o interface "ICloneable" nunca é chamado pela framework do .Net. O seu único propósito é fornecer uma forma padrão de disponibilizar um clone de um objecto. 

Demo: 

 Download Demo

IEnumerable 

Para que um tipo suporte enumeração através de um ciclo "For … Each" tem de implementar um interface "IEnumerable". 

Quando o compilador chega a um ciclo "For … Each" o código gerado pelo compilador invoca o único método deste interface "GetEnumerator". Este método deve retornar um objecto que suporte o interface "IEnumerator", que por sua vez expõe os três membros seguintes:   


  "MoveNext" – É um método, chamado a cada interacção e deve retornar "True" se existir um novo elemento disponivel e "False" se não existir nenhum novo elemento. 

  "Current"- É uma propriedade "Read-only" que devolve o valor a ser utilizado na interacção actual do ciclo. 

  
    "Reset" – É um método que faz "reset" ao ponteiro utilizado internamente para que o próximo valor a ser obtido seja o primeiro. 

       Demo: 

     Download Demo

     
  


Caso Prático 

Pretende-se desenvolver uma pequena aplicação que demonstre a utilização de interfaces da plataforma .Net bem como a implementação de novos interfaces. 

Esta aplicação será uma pequena agenda de contactos, mantendo uma lista de pessoas bem como os seus respectivos contactos. 

A lógica da aplicação é a seguinte:    


  São criados dois interfaces, que serão implementados pelas classes da aplicação: 

  É implementado o mecanismo de introdução de dados e sua manipulação, utilizando interfaces da plataforma .Net 

  É feita a apresentação de resultados na consola.  


Regras para desenvolvimento    


  Criar um novo projecto do tipo "ConsoleApplication" 

  
    Criar um novo interface chamado "IContact" que defina: 

    
      Uma propriedade "Morada" do tipo "string" 

      Uma propriedade "Telefone" do tipo "string" 

      Uma propriedade "Email" do tipo "string" 

      Uma propriedade "Titulo" do tipo String (esta propriedade irá conter o titulo do contacto, exemplo: "escritório", "casa", etc.) 
    
  

  
    Criar um novo interface chamado "IPerson" que defina: 

    
      Uma propriedade só de leitura "PrimeiroNome" do tipo "String" 

      Uma propriedade só de leitura "UltimoNome" do tipo "String" 

      Uma propriedade "Nacionalidade" do tipo "string" 

      Uma propriedade "Vip" do tipo "Boolean" 

      Uma propriedade "Enabled" do tipo "Boolean" 

      Um método "AlterarNome" que aceite como argumentos as "strings": "PrimeiroNome" e "UltimoNome" e devolva uma "string" 
    
  

  
    Criar uma nova classe chamada "Contact" com as seguintes características: 

    
      Implementação do interface "IContact" 

      Um construtor que atribua valores "default" às propriedades: "Morada", "Telefone", "Email", "Titulo" 
    
  

  
    Criar uma nova classe chamada "PersonContacts" com as seguintes características: 

    
      Implementação dos interfaces: "IPerson", "IComparable", "IEnumerable" e "ICloneable" 

      Criar um campo privado do tipo "array" de objectos "Contact" que irá possuir todos os contactos de uma pessoa 

      Criar um construtor que defina os valores iniciais para as propriedades "PrimeiroNome" e "ÚltimoNome" 

      Criar um método "AdicionarContacto" que aceite como argumentos todos os campos definidos para o construtor da classe "Contact". Este método deve instanciar um novo contacto e coloca-lo no "array" de contactos. 

      Codificar a implementação do interface "IPerson" 

      
        Codificar a implementação do interface "IComparable" 

        
          Implementar o método "CompareTo" de forma a executar comparações pelo "UltimoNome" 
        
      

      Codificar a implementação do interface "ICloneable" 

      
        Codificar a implementação do interface "IEnumerator" 

        
          
            Esta classe deverá possuir a capacidade de expor uma colecção de contactos, disponível através de um ciclo "For Each". Como tal o método "GetEnumerator" deve devolver um tipo que implemente o interface "IEnumerator" (exemplo:PersonContactEnumerator) com as seguintes características: 

            
              Receber no construtor o array com todos os contactos da classe 

              No método "MoveNext" avançar uma posição no array 

              O método "Current" devolve a objecto actual do array 
            
          
        
      
    
  

  
    No modulo "Main" implementar a seguinte lógica: 

    
      Criar um "array" de "PersonContacts" com 4 elementos 

      
        Para cada casa do "array" criar um novo "PersonContact" 

        
          Atribuir valores para as propriedades "Vip" e "Nacionalidade" 

          Apenas na primeira pessoa, adicionar o valor "False" à propriedade "Enabled" 

          Adicionar alguns contactos a cada pessoa 
        
      

      Ordenar o "array" de pessoas 

      Executar dois ciclos encadeados "For Each" para mostrar no ecrã todas as pessoas bem como os respectivos contactos 

      Criar um novo objecto do tipo "PersonContacts" e obter um clone do original 

      Mostrar na consola que o novo objecto é um clone (utilizando o operador "IS") 
    

    Solução 

    Download:Interfaces-Solucao.zip

       

      &lt;img src="http://feeds.feedburner.com/~r/AnkaanetArtigos/~4/HFzOTdKeekM" height="1" width="1"/&gt;</description><dc:creator>Migasp</dc:creator><pubDate>Fri, 20 Jun 2008 18:41:37 GMT</pubDate><guid isPermaLink="false">f1397696-738c-4295-afcd-943feb885714:21</guid><feedburner:origLink>http://www.ankaa.net/Artigos/tabid/114/articleType/ArticleView/articleId/21/Interfaces.aspx</feedburner:origLink></item><item><comments>http://www.ankaa.net/Artigos/tabid/114/articleType/ArticleView/articleId/20/Ciclo-de-vida-dos-objectos.aspx#Comments</comments><slash:comments>1</slash:comments><wfw:commentRss>http://www.ankaa.net/DesktopModules/DnnForge%20-%20NewsArticles/RssComments.aspx?TabID=114&amp;ModuleID=491&amp;ArticleID=20</wfw:commentRss><trackback:ping>http://www.ankaa.net/DesktopModules/DnnForge%20-%20NewsArticles/Tracking/Trackback.aspx?ArticleID=20&amp;PortalID=0&amp;TabID=114</trackback:ping><title>Ciclo de vida dos objectos</title><link>http://feedproxy.google.com/~r/AnkaanetArtigos/~3/QRdrXTtljDU/Ciclo-de-vida-dos-objectos.aspx</link><description>Introdução   Uma das partes mais importantes no desenvolvimento em .Net é o conhecimento da forma como a CLR ("Common Language Runtime") aloca e liberta memória, e sobretudo como os objectos são destruídos no final do seu ciclo de vida.   COM e "Reference Counter"   Antes de estudar a forma como o .Net lida com a destruição de objectos, é importante entender a forma como os objectos COM se comportam a este respeito.      Os objectos COM mantêm uma localização de memória conhecida como "reference Counter", que recebe o valor 1 quando o objecto é criado e uma referência é assignada a uma variável. Sempre que outra variável é assignada ao mesmo objecto, o "reference counter" é incrementado em uma unidade. Sempre que uma desta variáveis é assignada a "Nothing" o "reference counter" é reduzido em uma unidade.         Desta forma, o "refence counter" contém a cada instante o número de variáveis que apontam para um dado objecto. Sempre que uma variável deixa de apontar para um objecto, é verificado se o "reference counter" passa de 1 para 0. Se esta condição se verificar, o objecto é destruído. De certa forma pode afirmar-se que os objectos COM são responsáveis pela sua própria vida.   Esta gestão de vida de objectos, é pesada e propensa a erros (nas linguagens em que a gestão do "reference counter" é feita pelo programador), tendo um impacto negativo na "performance" das aplicações.   Para além das questões de performance, o principal problema da gestão de ciclo de vida dos componentes COM é conhecido como "referências circulares".   Uma "referência circular" ocorre quando dois componentes se mantêm vivos por possuírem variáveis que se referenciam um ao outro, não estando a ser utilizadas por nenhuma outra variável da aplicação. Quando isto ocorre, a memória apenas é libertada quando a aplicação termina. Este é o principal responsável por problemas de "memory leakage" em componentes COM.   O .Net foi desenhado com o objectivo de anular definitivamente a necessidade de manter um "reference counter" e todos os problemas a este associados. Os objectos criados em .Net não possuem "reference counter", mas são mantidos numa área especial de memória designada "managed heap". Esta implementação implica que os objectos deixam de ser responsáveis pela gestão do seu ciclo de vida, sendo outro elemento, o "GC – garbage collection process" o responsável por esta gestão.   "Garbage Collection Process"   A gestão de memória na "framework" .net depende de um processo complexo, denominado "Garbage Collection". Quando uma aplicação tenta alocar memória para um novo objecto, e o "heap" não tem espaço livre suficiente, a "framework" inicia o processo de "garbage collection".   O processo de "garbage collection" é realizado internamente por um objecto denominado "garbage collector". Este objecto percorre todos os objectos existentes no "heap" e marca os que estão actualmente referenciados por pelo menos uma variável da aplicação (este processo reconhece objectos referenciados indirectamente a partir de outros objectos). Após marcar todos os objectos referenciados, o "garbage collector" destrói todos os outros objectos, uma vez que não são acedidos nem directamente nem indirectamente de qualquer variável da aplicação.      Após libertar os objectos não utilizados, o "garbage collector" compacta o "heap" e disponibiliza a memória disponível para novos objectos.      Na grande maioria dos casos, a forma de lidar com o ciclo de vida dos objectos no .Net é significativamente mais eficiente que a utilizada nos componentes COM. Contudo introduz um novo problema: "Nondeterministic Finalization".   Um objecto COM controla o momento em que o seu "reference counter" passa de 1 para 0, ou seja, controla o momento em que deixa de ser utilizado pela aplicação. Quando esse momento surge, é disparado o evento "Class_terminate"(ou executado o "destructor" no caso do c++) e o código aqui existente pode proceder as acções de limpeza de objectos e libertação de recursos (como por exemplo ficheiros).   No caso do .Net, não existe controlo sobre o momento em que o objecto vai ser libertado, apenas se sabe que será algum tempo após a última variável que aponta para ele ser igualada a "Nothing". Assim não é possível utilizar no .Net um equivalente ao evento semelhante ao "Class terminate".   Torna-se assim necessário distinguir entre destruição lógica de um objecto (quando a aplicação liberta a última variável que o utiliza) e a sua destruição física (quando o objecto é realmente removido da memória).   "Nondeterministic Finalization"   Se a memória fosse o único recurso utilizado pelos objectos, a falta de controlo sobre o momento da destruição não seria propriamente um problema, pois a gestão de memória poderia garantir a sua reorganização sempre que uma aplicação necessitasse de um bloco maior.   A razão do problema deve-se a que os objectos normalmente utilizam outro tipo de recursos, tal como ficheiros, conexões a bases de dados, ligações a portas série ou paralelas, objectos internos, etc. Quando este tipo de recursos é utilizado, a sua libertação tem de ser feita com a maior urgência possível, de forma a garantir que outra parte da aplicação, ou outras aplicações os possam utilizar.   A solução para o problema passa por encontrar uma forma de executar código de limpeza de recursos quando os objectos são destruídos. Esta solução foi implementada através de dois métodos especiais: "Finalize" e "Dispose".   "Finalize"   O método "Finalize" é um método especial que é chamado pelo "garbage collector" exactamente antes de libertar a memória utilizada pelo objecto, ou seja, quando o objecto é fisicamente destruído. É o mecanismo mais semelhante ao evento "Class_terminate" (ou aos "destructors"), mas pode ser desencadeado vários minutos, ou mesmo horas após a libertação lógica dos recursos.   O método "Finalize" faz parte da classe "system.object" pelo que é herdado por todos os objectos da plataforma (Todos os objectos da plataforma herdam de "system.object"). Assim, para que seja possível incluir um "Finalize" personalizado para um objecto especifico, é necessário incluir as expressões "Overrides" e "Protected".      Protected Overrides Sub Finalize() 
    Debug.Writeline("O objecto vai ser destruido") 
End Sub


O "Finalize" nunca deve ser utilizado para aceder a nenhum outro objecto, pois esse objecto pode já ter sido destruído (A ordem pela qual os objectos são reclamados e destruídos pelo "garbage collector" é impossível de prever). O único objecto que pode ser utilizado com segurança a partir do método "finalize" é o objecto base, através do "MyBase". 

Como regra geral, é seguro invocar métodos estáticos a partir do método "finalize", excepto quando a aplicação esta a ser terminada, pois neste caso a plataforma .Net pode já ter destruído o tipo que define o objecto estático (Por exemplo, não deve ser utilizado o console.writeline pois o objecto "Console" pode já ter sido destruído. O objecto "Debug" é sempre um dos últimos objectos a ser destruído, sendo por isso preferível). 
  

  No entanto é possível determinar se a aplicação esta a ser terminada: 


  Protected Overrides Sub Finalize() 
    If Not Environment.HasShutDownStarted Then 
        'É seguro aceder a métodos estáticos 
    End If 
End Sub


Os objectos que expõem um método "Finalize" normalmente necessitam de dois ciclos do "garbage collection" para serem reclamados e a sua memória libertada. Isto acontece pois o código dentro do método "finalize" pode assignar o objecto corrente (através do "Me") a uma variável global. Se o objecto fosse reclamado nesta fase a variável global ficaria com uma referência inválida. 

A criação de objectos com um método "finalize" requer mais ciclos de CPU. É importante compreender que a finalização de objectos tem um custo, e como tal deve ser utilizada apenas quando for realmente necessária. 

"Dispose" 

O método "Dispose" permite colmatar a falta de verdadeiros destrutores no .Net. 

Por convenção, uma classe bem desenhada deve expor um método chamado "Dispose", que permite a clientes "bem comportados" libertar manualmente todos os recursos utilizados (tal como ficheiros, ligações a bases de dados, etc.). Este método deve ser chamado pelo cliente, imediatamente antes de libertar a variável que aponta para o objecto, em vez de ficar à espera do próximo ciclo do "garbage collection". 

As classes que disponibilizarem este método, tem de implementar o interface "IDisposable" que expõe apenas o método "Dispose". 


  Public Class Person 
    Implements IDisposable 
    Public Sub Dispose() Implements IDisposable.Dispose 
        'Código de limpeza 
    End Sub 
End Class


Exemplo de utilização da classe anterior por um cliente "bem comportado": 


  'Cria o objecto 
Dim obj As New Person 
'Utiliza o objecto … 
'….. 
'Liberta os recursos 
Obj.dispose 
'Destrói a variável 
Obj=Nothing


O Visual Basic permite a utilização do "Using", que garante que o método "Dispose" é chamado automaticamente, mesmo que o código gere uma excepção. A única limitação é que a excepção não é apanhada para tratamento especifico: 


  'Cria o objecto 
Using obj as new person() 
'Utiliza o objecto … 
'….. 
End Using


Por norma, o método "dispose" de um objecto deve invocar o método "dispose" de todos os objecto utilizados internamente, e que estão "escondidos" do cliente. 

Por exemplo, se a classe "Person" utilizasse internamente um objecto "System.timers.timer", o método "dispose" da classe "Person" deveria chamar o método "dispose" do objecto "Timer": 


  Public Class Person 
    Implements IDisposable 
    Public Sub Dispose() Implements IDisposable.Dispose 
        'Código de limpeza 
        MyTimer.Dispose() 
    End Sub 
End Class


A implementação da regra definida no ponto anterior, e o facto de alguns objectos serem partilhados por múltiplos clientes pode levar a que o método "dispose" seja chamado mais do que uma vez. 

Por definição, o método "Dispose" não deve gerar qualquer erro se for chamado mais do que uma vez, embora deva ignorar todos os pedidos feitos após o primeiro ser recebido (Isto pode ser conseguido utilizando uma variável da classe): 


  Public Class Person 
    Implements IDisposable 
    Private Disposed As Boolean = False 
    Public Sub Dispose() Implements IDisposable.Dispose 
        If Disposed Then Exit Sub 
        Disposed = True 'Garante que só executa uma vez 
        'Código de limpeza 
    End Sub 
End Class


Por definição, se for chamado um método que não o "dispose" num objecto para o qual já foi chamado o "Dispose" pelo menos uma vez, deve ser gerada uma excepção do tipo "ObjectDisposeException". Este comportamento pode ser obtido utilizando a mesma variável da classe: 


  Public Class Person 
    Implements IDisposable 
    Private Disposed As Boolean = False 
    Public Sub UmMetodo() 
        If Disposed Then Throw New ObjectDisposedException("Person") 
        '… 
    End Sub 
    Public Sub Dispose() Implements IDisposable.Dispose 
        If Disposed Then Exit Sub 
        Disposed = True 'Garante que só executa uma vez 
        'Código de limpeza 
    End Sub 
End Class


Combinar "Dispose" e "Finalize" 

Tipicamente, uma recurso é alocado de uma de duas formas (á excepção da memória):    


  Invocando código "unmanaged" (por exemplo ao utilizar uma função da API do windows) 

  Através da criação de uma instancia de um objecto da framework .Net que expõe o recurso 


A técnica utilizada condiciona a decisão sobre qual o melhor local para libertar recursos "Dispose" ou "Finalize":    


  Se for alocado um recurso que não memória, é necessário implementar o método "Dispose" (independentemente de se o recurso foi alocado através de código "managed" ou "unmaged". 

  O método "Finalize" apenas tem de ser implementado se o objecto alocar um recurso "unmanaged" directamente. 



  Nota: A implementação dos métodos "Dispose" ou "Finalize" apenas é realmente obrigatória se a classe possuir uma referência para o recurso através de uma variável da classe. Se cada método alocar e desalocar os seus próprios recursos (por exemplo com "Using", não é realmente necessário implementar qualquer uma destas técnicas. 


A decisão sobre a técnica de finalização mais adequada pode variar pelos seguintes casos:    


  Nem o "Dispose" nem o "Finalize": Se a classe utiliza apenas memória ou outros recursos que não necessitam desalocação explicita, ou se cada método alocar e desalocar os seus recursos internamente. 

  Apenas o "Dispose": Se a classe aloca recursos para além da memória, através de outros objectos .Net, e se pretende providenciar aos clientes uma forma de libertar estes recursos o mais rápidamente possivel. 

  Ambos "Dispose" e "Finalize": Se a classe aloca um recurso directamente (tipicamente atarvés de uma DLL "unmaged") que requer desalocação explicita. A desalocação explicita é realizada no método "Finalize" mas o método "Dispose" permite ao cliente libertar os recursos de imediato.

  Apenas o "Finalize": Se a classe não tem nenhum recurso para libertar, mas é necessário realizar algum passo quando o objecto é finalizado (não é um cenário comum). 


Exemplo do caso descrito no ponto 3 (Ambos "Dispose" e "Finalize"): 


  Public Class ClipboardWrapper 
    Implements IDisposable : Dim isOpen As Boolean 
    Private Declare Function OpenClipboard Lib "user32" Alias "OpenClipboard" (ByVal hwnd As Integer) As Integer 
    Private Declare Function CloseClipboard Lib "user32" Alias "CloseClipboard" () As Integer 
    Public Sub Open(ByVal hWnd As Integer) 
        If OpenClipboard(hWnd) = 0 Then Throw New Exception("Unable to open clipboard") 
        isOpen = True 
    End Sub 
    Public Sub Close() 
        If isOpen Then CloseClipboard() 
        isOpen = False 
    End Sub 
    Private Sub Dispose() Implements IDisposable.Dispose 
        Close() 
    End Sub 
    Protected Overrides Sub Finalize() 
        Close() 
    End Sub 
End Class


"SuppressFinalize" 

A técnica apresentada no exemplo anterior ainda não é perfeita. Neste caso o "garbage collector" irá chamar o método "finalize" quer o cliente tenha chamado o método "dispose" (ou o "close", também válido neste caso) ou não. 

Como já foi descrito, o método "finalize" afecta negativamente a performance, como tal, em casos idênticos ao anterior (em que se utiliza o "Dispose" e o "Finalize"), deve ser utilizado um método da classe "GC", o "SupressFinalize", a partir do método "Dispose". Desta forma, se o cliente for "bem comportado" e chamar o método "Dispose", o "garbage collector" já não irá voltar a executa-lo no método "Finalize". 


  Public Class Person 
    Implements IDisposable 
    Private Disposed As Boolean = False 
    Public Sub Dispose() Implements IDisposable.Dispose 
        If Disposed Then Exit Sub 
        Disposed = True 'Garante que só executa uma vez 
        'Código de limpeza 
        GC.SupressFinalize(Me) 
    End Sub 
End Class


Melhorar o mecanismo "Dispose" - "Finalize" 

Outro problema deixado em aberto no exemplo anterior, e que pode ocorrer em classes mais complexas, é que o código de limpeza pode aceder a outros objectos referenciados pelo objecto corrente, e este código nunca deve ser executado a partir do método "Finalize" pois como descrito anteriormente, os objectos podem já não existir. 

Esta situação pode ser resolvida movendo o código de limpeza para uma versão "overloaded" do método "dispose", que aceite um argumento booleano que identifica se a limpeza esta a ser feita a partir do "Dispose" ou do "finalize". 


  Public Sub Close() 
    Dispose(True) 
End Sub 

Private Sub Dispose() Implements IDisposable.Dispose 
    Dispose(True) 
    'Identifica como já tendo sido "Disposed" 
    Disposed = True 
    'Evita que o "Finalize" seja chamado 
    GC.SupressFinalize(Me) 
End Sub 

Protected Overrides Sub Finalize() 
    Dispose(False) 
End Sub 

Protected Overridable Sub Dispose(ByVal Disposing As Boolean) 
    If Disposed Then Exit Sub 
    If Disposing = True Then 
        'Aqui é seguro aceder a outros objectos !! 
        ' … 
    End If 
    'Aqui pode ser colocado o código que pode ser executado nos dois casos 
End Sub


O .Net permite implementar o "IDisposable Interface" de uma forma bastante rápida, gerando todo o template automáticamente, bastando escrever "Impements IDisposable". 

De notar que nenhum "Finalize" é criado automaticamente. Este é o comportamento correcto, pois tal como já foi referido, apenas as classes que utilizam recursos "unmanaged" necessitam deste método, e uma vez que o seu uso tem impacto negativo na performance, a sua utilização não é o "default". 

"Finalizers" em classes derivadas 

Se uma classe herdar de outra classe, em que os métodos "Dispose" e "Finalize" estão devidamente implementados, e esta classe derivada não alocar nenhum novo recurso "unmanaged", não é necessário qualquer esforço adicional, uma vez que os métodos definidos na classe base são herdados pela nova classe. 

Contudo, se a nova classe alocar novos recursos "unmanaged" é necessário redefinir estes métodos, de forma a libertar os recursos usados pela classe derivada. 

Seguindo o modelo do exemplo anterior, em que apenas um método era utilizado, tanto para o "dispose" como para o "finalize", apenas é necessária a redefinição de um método. 


  Protected Overrides Sub Dispose(ByVal disposing As Boolean) 
    If Disposed Then Exit Sub 
    Try 
        If disposing Then 
            ' The object is being disposed, not finalized. It is safe to access other 
            ' objects (other than the base object) only from inside this block. 
            '… 
        End If 
        ' Perform clean up chores that have to be executed in either case. 
        '… 
    Finally 
        ' Call the base class's Dispose method in all cases. 
        MyBase.Dispose(disposing) 
    End Try 
End Sub



    Caso prático 

Pretende-se desenvolver uma pequena aplicação que demonstre o ciclo de vida dos objectos .Net 

A lógica da aplicação é a seguinte:    


  É desenvolvida uma classe chamada "Creatures", que implementa os métodos "dispose" e "finalize" e que contabiliza o número de instâncias: criadas, vivas, "Zombies" (mortas pela aplicação mas ainda não mortas pelo "Garbage collector") e mortas 

  Um "form" que apresenta a informação ao utilizador.    


Regras para desenvolvimento    


  Criar um novo projecto com o template "Windows Application" 

  
    Criar uma classe chamada "Creature" que implementa o interface "IDisposable" com as seguintes características: 

    
      Um conjunto de variáveis estáticas, chamadas:Criados, Vivos, Zombies e mortos do tipo inteiro. 

      Um construtor, onde são incrementadas as variáveis "Criados" e "Vivos" 

      Um método "dispose" onde é retirada uma unidade a variável "Vivos" e adicionada uma a variável "Zombies" 

      Um método "finalize" onde é retirada uma unidade à variável "Zombies" e adicionada à variável "Mortos" 
    
  

  
    Criar um "Form" com as seguintes características: 

    
      
        Desenhar o "form" de forma a: 

        
          Apresentar um conjunto de labels para os valores das variáveis "Criados", "Vivos", "Zombies" e "Mortos". 

          Apresentar duas caixas de texto para introdução do tempo para criação de criaturas e de destruição de criaturas. 

          Um botão que inicia a criação e destruição de criaturas. 

          Um botão que termina apenas a criação de novas criaturas. 

          Criar 3 "Timers" (Um para controlar a criação de criaturas, um para controlar a sua destruição e um terceiro para controlar a apresentação dos valores no "form") 
        
      

      
        Internamente, o "form" deverá possuir a seguinte lógica: 

        
          Criar um "array" de criaturas que irá conter todas as criaturas criadas. 

          No botão "Iniciar" activar os "timers" de criação e de destruição com os valores definidos pelo utilizador 

          No botão "Terminar" interromper o ciclo do "Timer" de criação de criaturas. 

          A cada ciclo do "timer" de criação de criaturas, criar uma nova criatura e coloca-la no array. 

          No ciclo que controla a remoção de criaturas, escolher aleatoriamente uma criatura do array, destruí-la através do método "Dispose" e igualando a variável a "Nothing". Remover a variável do array. 

          No ciclo que controla a apresentação de informação, actualizar as labels (Este timer deverá ter um intervalo constante de 100 ms). 
        
      
    

    Sugestão de apresentação do Form 

     

    Solução 

    Download:CicloDeVida-Solucao.zip

       

       &lt;img src="http://feeds.feedburner.com/~r/AnkaanetArtigos/~4/QRdrXTtljDU" height="1" width="1"/&gt;</description><dc:creator>Migasp</dc:creator><pubDate>Fri, 20 Jun 2008 18:09:11 GMT</pubDate><guid isPermaLink="false">f1397696-738c-4295-afcd-943feb885714:20</guid><feedburner:origLink>http://www.ankaa.net/Artigos/tabid/114/articleType/ArticleView/articleId/20/Ciclo-de-vida-dos-objectos.aspx</feedburner:origLink></item><item><comments>http://www.ankaa.net/Artigos/tabid/114/articleType/ArticleView/articleId/19/Heranccedila.aspx#Comments</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.ankaa.net/DesktopModules/DnnForge%20-%20NewsArticles/RssComments.aspx?TabID=114&amp;ModuleID=491&amp;ArticleID=19</wfw:commentRss><trackback:ping>http://www.ankaa.net/DesktopModules/DnnForge%20-%20NewsArticles/Tracking/Trackback.aspx?ArticleID=19&amp;PortalID=0&amp;TabID=114</trackback:ping><title>Heran&amp;ccedil;a</title><link>http://feedproxy.google.com/~r/AnkaanetArtigos/~3/NBmp1-_V7Pk/Heranccedila.aspx</link><description>Introdução   Classes podem ser criadas herdando (derivando) de uma classe previamente definida, ou da Framework .Net.   A classe derivada fica por herança com todos os campos, propriedades, métodos, e eventos da classe base .   Todas as classes da plataforma são derivadas de System.Object.   Herança – "Overriding"   Uma classe derivada de outra classe pode alterar ("Override") o comportamento de um método ou propriedade definido na classe base. Para isso o método da classe base tem de possuir a palavra chave "Overridable":      'Classe base 
Public Class Person 
    ' Fields Visiveis fora da classe 
    Public FirstName As String 
    Public LastName As String 
    Public Overridable Function CompleteName() As String 
        Return FirstName &amp; " " &amp; LastName 
    End Function 
End Class


A classe derivada tem de possuir a palavra chave "Overrides": 


  'Classe derivada 
Public Overrides Function CompleteName() As String 
    Return LastName &amp; ", " &amp; FirstName 
End Function


Regras para "overriding" 


  Ao redefinir uma classe não é possível alterar os atributos "ReadOnly" ou "WriteOnly". 
      

  Se for feito o "overriding" do membro default na classe base, esse membro tem de ser o o membro default da classe derivada (e tem de ser marcado com "Default"). 
      

  Não é possível fazer "override" de campos, constantes, eventos e de membros estáticos. 
      

  Um método definido como "Overrides" é automáticamente "Overridable" (Caso não se pretenda aceitar este comportamento, deve utilizar-se "NotOverridable". 
      

  Se se pretender implementar na classe derivada um método com o mesmo nome mas com uma assinatura diferente de um existente na classe base, é necessário utilizar a expressão "Overloads" (Neste caso não é um "Override" mas sim um "Overload"). 


MyBase 

A palavra reservada "MyBase" permite o acesso a campos, propriedades ou métodos da classe base.  

  Se um membro não tiver sido "overridden" na classe derivada, as expressões: 

"Me.membername" e "MyBase.membername" referem-se ao mesmo membro e executam o mesmo código. 

Se o membro tiver sido "overridden" na classe derivada, é necessário utilizar "MyBase" para aceder ao membro tal como ele foi definido na classe base. 


  'Na classe base 
Public Overridable Function CompleteName() As String 
    Return FirstName &amp; " " &amp; LastName 
End Function


   


  'Na classe Derivada 
Public Overrides Function CompleteName() As String 
    Dim result As String = MyBase.CompleteName 
    If Title  "" Then result = Title &amp; " " &amp; result 
    Return result 
End Function


Construtores 

Se a classe base possuir um construtor sem argumentos ou não possuir um construtor explicito, não é necessário definir um construtor explicito na classe derivada. 

Se a classe base possuir um construtor com argumentos, a classe derivada tem de possuir um método construtor, e a primeira instrução deste método tem de ser uma chamada ao construtor da classe base: 


  'Classe Base 
Public Class person 
    Dim FirstName As String 
    Dim LastName As String 
    Sub New(ByVal firstname As String, ByVal lastname As String) 
        Me.FirstName = firstname 
        Me.LastName = lastname 
    End Sub 
End Class


   


  'Classe derivada 
Public Class employee 
    Inherits person 
    Sub New(ByVal FirstName As String, ByVal LastName As String, ByVal Title As String) 
        MyBase.new(FirstName, LastName) 
        '... 
    End Sub 
End Class


 

 Download demo

MyClass 

Quando é chamado um método que não foi "overridden" numa classe derivada, o código executado é o definido na classe base, contudo este é executado no contexto da classe derivada. 

Por exemplo, se uma classe utilizar um método da classe base, e esse método aceder a uma propriedade "overriden", o código da propriedade utilizado é o definido na classe derivada. 

Demonstração: 

 Download Demo

Para garantir que o código definido numa classe base utiliza os membros definidos na sua própria estrutura, e não os "overriden" na classe derivada, é utilizada a "MyClass". 

A não utilização da "MyClass" pode originar "bugs" de resolução lógica complicada: 


  Public ReadOnly Property CanVote() As Boolean 
    Get 
        Return (MyClass.Age &gt;= 18) 
    End Get 
End Property


 Download Demo

Member Shadowing 

Necessidade do "Member Shadowing": 

O .Net permite que a herança seja feita mesmo a partir de uma classe compilada (DLL) para a qual não é possível ver ou controlar o código fonte. Mas o que acontece se uma classe derivada implementar um método ou propriedade, e mais tarde, o autor da classe base disponibilizar uma nova versão com um método ou propriedade com o mesmo nome ? 
  

  O VB aceita que a classe derivada tenha um membro com o mesmo nome de um membro da classe base, mas origina um aviso. No entanto disponibiliza outras formas de trabalhar o "Shadowing" de membros: 

    


  Método default: Um membro de uma classe derivada esconde todos os membros da classe base, independentemente da sua assinatura. 

  Um membro da classe derivada, marcado como "Shadows" esconde todos os membros da classe base, tal como descrito no ponto anterior, mas não origina qualquer aviso. 

  Um membro da classe derivada definido com a palavra "Overloads" esconde apenas o membro da classe base com o mesmo nome e a mesma assinatura. 


Redefinição de membros estáticos 

Os membros estáticos não podem ser redefinidos ("Overridden"). Este membros são herdados ou são escondidos ("Shadowed") e recriados de raiz na classe derivada. 

Ao recriar de raiz um método estático na classe derivada, não é possível utilizar a expressão "MyBase" para aceder aos métodos estáticos definidos na classe base. 


  'Metodo na classe Person 
Public Shared Function AreBrothers(ByVal e1 As Person, ByVal e2 As Person) As Boolean 
    Return (p1.Father Is p2.Father) Or (p1.Mother Is p2.Mother) 
End Function


   


  'Metodo na classe Employee 
Public Shared Shadows Function AreBrothers(ByVal e1 As Employee, ByVal e2 As employee) As Boolean 
    Return person.AreBrothers(e1, e2) And (e1.LastName = e2.LastName) 
End Function


Herança – Classes"Sealed" 

É possível criar uma classe que não possa ser derivada, utilizando a expressão "NotInheritable". Este tipo de classes é designada por "Sealed classes". 

A criação de classes sealed não é comum, mas é útil nalguns casos, como por exemplo: 


  Classes com métodos "utilitários" que apenas expõem membros estáticos. 

  Classe imutáveis, que só possuem propriedades de leitura e não podem ser modificadas após instanciação. 



  Public NotInheritable Class Employee 
    '… 
End Class


Classes"Abstractas" 

É possível criar uma classe que não pode ser instanciada directamente, mas que obriga a que seja criada uma nova classe que a derive. Este tipo de classes é designado por classes Abstractas. 

Uma classe abstracta é definida com a expressão "MustInherit". 

Este tipo de classes é normalmente utilizado para definir um comportamento ou um modelo de objecto, que nunca existe na forma abstracta (Por exemplo uma classe animal, que seria depois derivada como cão, gato, etc.) 


  Public MustInherit Class Document 
    '… 
End Class
Apesar da classe estar marcada como "MustInherit" continua a ser necessário utilizar as expressões "Overridable" e "Overrides", pois a classe base pode conter também membros que não podem ser redefinidos. 

Como regra geral, uma classe que derive de uma classe abstracta não é obrigada a redefinir ("Override") os membros da classe base. Mas por vezes o código da classe base é criado especificamente de forma de a obrigar a classe derivada a definir uma versão personalizada de um membro. Este comportamento é obtido com a expressão "MustOverride". 


  Public MustInherit Class Shape 
    'Coordenadas x e y 
    Public x, y As Single 
    'Metodo que move o objecto no plano 
    Public Sub OffSet(ByVal deltaX As Single, ByVal deltaY As Single) 
        x = x + deltaX 
        y = y + deltaY 
        'Redesenha o objecto na nova posição 
        Display() 
    End Sub 
    Public MustOverride Sub Display() 
End Class


Ao definir um membro como "MustOverride", é obrigatório definir apenas a sua assinatura (é omitida qualquer implementação e as expressões "End Property", "End Sub", "End Function". 

Se uma classe possuir pelo menos um método "MustOverride", tem de ser obrigatoriamente definida como abstracta ("MustInherit"). 

Exemplo de uma classe que deriva da classe "Shape" do exemplo anterior: 


  Public Class Square 
    Inherits Shape 
    Public Side As Single 
    Public Overrides Sub Display() 
        'Implementação … 
    End Sub 
End Class


"Nested Classes" 

É possível criar classe encadeadas, da seguinte forma: 


  Class Outer 
    Class Inner 
    End Class 
End Class


O código existente dentro da class "Outer" pode sempre criar instancias da class "inner", independentemente do "scope" definido para esta. 

Ao contrário das classes de topo, uma classe encadeada pode ser definida com o "scope" "private", estando neste caso disponível apenas a partir da classe pai. 

Se a classe encadeada não for definida com o "scope" "private", fica visível e disponível, tal como a classe "outer", ex: 

Dim Obj As New Outer.Inner() 

A utilização de classes encadeadas pode ser útil em vários casos, como por exemplo:  


  Para organização de classes em grupos relacionados (semelhante ao que se obtém com "namespaces" explícitos).  

  Para encapsular classes auxiliares dentro das classes principais que as utilizam, tornando-as invisíveis para o resto da aplicação (neste caso a classe interna deve estar definida com o "scope" "Private". 


"Scope Qualifiers" 

"Scope Qualifiers" que não estão relacionados com a herança: 


  "Public" – Torna a classe ou um dos seus membros visível a partir de fora do "Assembly" que a define. 

  "Friend" – Torna a classe ou um dos membros visíveis dentro do "assembly" actual. Na maioria das vezes, um "assembly" corresponde a um projecto, por isso na prática, significa que o membro esta disponível para o projecto em que esta definido. Este é o "Scope" default das clases. 

  "Private" – Torna a classe (apenas se for encadeada) ou o membro visível apenas na classe que o contém. 


"Scope Qualifiers" que estão relacionados com a herança:    


  "Protected" – Torna uma classe (se for encadeada) ou um membro visível a partir da classe actual e de classes que derivem da classe actual. Normalmente é utilizado quanto não se pretende expor um membro a clientes regulares da classe, mas se permite disponibilizar uma forma para que clientes que herdem a classe possam alterar o seu comportamento. 

  "Protected Friend" – Combina as características dos "Friend" e "Protected", definindo um membro ou uma classe encadeada que é visível dentro do "assembly" bem como em todas as classes derivadas. 



  Nota: Não é possível utilizar "Scope Qualifiers" para alterar o "scope" de um método redefinido ("Overriden"). Por exemplo, se a classe base contém um método público, não é possível redefini-lo como "Private" ou "Friend" na classe derivada. 


Redefinição de eventos 

Não é possível fazer a redefinição de eventos da mesma forma que se procede com propriedades ou métodos (Não é possível utilizar a expressão "Overrides" com eventos, no entanto a expressão "Shadows" é legal). 

Por vezes é necessário a uma classe derivada alterar o comportamento de eventos na classe base, por exemplo para executar processamento adicional quando é disparado um evento na classe base, ou para anular alguns dos eventos da classe base. Estas implementações são possíveis, seguindo diferentes abordagens: 


  
    Se a classe derivada apenas necessita de receber uma notificação quando um evento é disparado na classe base, basta adicionar um "handler" para o evento utilizando o "MyBase": 
      

    
      Public Class Form1 
    Inherits System.Windows.Forms.Form 
    Private Sub Form1_Load(ByVal Sender As Object, ByVal e As EventArgs) Handles MyBase.Load 
        'Código … 
    End Sub 
End Class
    

     
  

  
    A técnica apresentada no exemplo anterior não exige qualquer alteração na classe base, mas tem uma séria limitação: A classe derivada não tem qualquer controlo sobre o próprio evento, sobre os seus argumentos, ou a capacidade para evitar o seu disparo. Para resolver esta limitação, é necessário alterar a forma como a classe base dispara eventos, ou seja, a classe base tem de ser originalmente desenhada com o objectivo de ser herdada.  

      Em vez de se utilizar o "RaiseEvent" para disparar um evento, a classe base deve chamar um método "Overridable", que por convenção se deve chamar "OnEventName. 

      

    
      Protected Overridable Sub OnNameChanged(ByVal e As EventArgs) 
    RaiseEvent NameChanged(Me, e) 
End Sub
    

    
      Nota: A classe derivada não pode utilizar o "RaiseEvent" directamente para disparar um evento definido na classe base. A única forma é despoletar o evento indirectamente através do método "OnNameChanged" da classe base. Por razões de consistência, os métodos "OnEventName" devem sempre aceitar um argumento do tipo "EventArgs" (ou seu derivado). 
    

    Exemplo: 

     Download Demo

    Caso prático 

    Pretende-se desenvolver uma pequena aplicação que escreva "logs" simples, em ficheiros de texto e no "event viewer". 

    Esta aplicação irá por em prática alguns dos conhecimentos adquiridos acerca da herança. 

    A lógica da aplicação é a seguinte: 
  

  É desenvolvida uma classe Abstracta que será a base para futuras classes de escrita de "logs". 

  
    São desenvolvidas duas classes baseadas na classe abstracta. Uma escreve logs em ficheiros de texto e a outra no "event viewer". 

    Regras para desenvolvimento 

    Criar um novo projecto com o template "Console Application" 
  

  
    Criar uma classe "Log" com as seguintes características: 

    
      Um construtor, que aceite como argumento uma string, que será o nome da categoria a ser utilizada. 

      
        Um propriedade chamada "Categoria", que será afectada pelo valor recebido pelo construtor. O "Set" da categoria deverá implementar a seguinte lógica: 

        
          Se a categoria for "mensagens", "avisos" ou "erros" atribuir o valor recebido. 

          Se for outro valor, atribuir o valor "geral" 
        
      

      Um método "Overridable" chamado "GetDate" que devolve a data actual. 

      Um método "MustOverride" chamado "WriteLogLine" que recebe uma "string" e que será implementado por cada uma das classes derivadas. 
    
  

  
    Criar uma classe derivada da classe "Log" chamada "LogFile" com as seguintes características: 

    
      Um construtor que aceita a categoria. 

      Um método que faz a implementação do método "WriteLogLine" definido na classe base, escrevendo num ficheiro com o nome categoria.txt (categoria é o valor da propriedade), e escrevendo a data (obtida a partir da função "GetDate" da classe base. 
    
  

  Criar uma classe chamada "LogEventViewer" com comportamento idêntico à classe "LogFile" mas que implementa o método "WriteLogLine" escrevendo no "event viewer" 

  
    No modulo main, criar um objecto de cada tipo e escrever uma mensagem. 

    Solução 

    DownloadHeranca-Solucao.zip

       

      &lt;img src="http://feeds.feedburner.com/~r/AnkaanetArtigos/~4/NBmp1-_V7Pk" height="1" width="1"/&gt;</description><dc:creator>Migasp</dc:creator><pubDate>Fri, 20 Jun 2008 17:37:46 GMT</pubDate><guid isPermaLink="false">f1397696-738c-4295-afcd-943feb885714:19</guid><feedburner:origLink>http://www.ankaa.net/Artigos/tabid/114/articleType/ArticleView/articleId/19/Heranccedila.aspx</feedburner:origLink></item><item><comments>http://www.ankaa.net/Artigos/tabid/114/articleType/ArticleView/articleId/16/Tratamento-de-eventos-e-quotDelegatesquot.aspx#Comments</comments><slash:comments>1</slash:comments><wfw:commentRss>http://www.ankaa.net/DesktopModules/DnnForge%20-%20NewsArticles/RssComments.aspx?TabID=114&amp;ModuleID=491&amp;ArticleID=16</wfw:commentRss><trackback:ping>http://www.ankaa.net/DesktopModules/DnnForge%20-%20NewsArticles/Tracking/Trackback.aspx?ArticleID=16&amp;PortalID=0&amp;TabID=114</trackback:ping><title>Tratamento de eventos e &amp;quot;Delegates&amp;quot;</title><link>http://feedproxy.google.com/~r/AnkaanetArtigos/~3/uokLd44B4q0/Tratamento-de-eventos-e-quotDelegatesquot.aspx</link><description>Objectivo   Este artigo apresenta os "delegates" e as técnicas para expor e consumir eventos. No final será apresentado um caso prático, que consiste num simulador mais realista do processo de extração de bolas do "Euromilhões" (este exercício é uma evolução do apresentado no artigo sobre OOP disponível aqui)   "Delegates"   Um "Delegate" é um objecto que permite invocar um método indirectamente.    Os "Delegates" podem ser utilizados para invocar quer métodos estáticos quer métodos de instância.    Os "Delegates" são o motor de implementação interna dos eventos.    Exemplo de declaração de um "Delegate":          Delegate Sub DisplayMessage (ByVal msg as String) 



Exemplo de instanciação de um objecto to tipo "Delegate": 



Dim MyDelegate as DisplayMessage 
MyDelegate= new DisplayMessage (AdressOf WriteToDebugWindow)



O procedimento utilizado neste exemplo "WriteToDebugWindow", tem de ter a mesma assinatura que o "delegate" utilizado ("DisplayMessage"): 



' Exibe uma mensagem na janela de debug 
Sub WriteToDebugWindow(ByVal msgText As String) 
Debug.WriteLine(msgText) 
End Sub



Exemplo de invocação de um objecto to tipo "Delegate": 
  



Dim MyDelegate as DisplayMessage 
MyDelegate= new DisplayMessage (AdressOf WriteToDebugWindow)
MyDelegate.Invoke ("teste") 




  A flexibilidade oferecida por um "delegate" permite alterar o comportamento de uma aplicação com alteração minima de código: 

  



Dim MyDelegate as DisplayMessage 
MyDelegate= new DisplayMessage (AdressOf WriteToPopUpWindow)
MyDelegate.Invoke ("teste")




  Exemplo de implementação do método "WriteToPopUpWindow": 



' Exibe uma mensagem numa janela pop up 
Sub WriteToPopUpWindow(ByVal msgText As String) 
MsgBox(msgText) 
End Sub



Os eventos são processados internamente através de "Delegates". Uma classe que expõe um evento, define internamente um "delegate" privado que aponta para todos os clientes que subscreveram o evento. Quando o evento dispara, a infra-estrutura do .Net chama o método "invoke" do "delegate" levando a que todos os clientes sejam notificados do evento. 

Tratamento de Eventos (Design Time) 

Um "Handler" para um evento é criado automaticamente, por exemplo quando se clica no objecto do tipo Button: 



Private Sub ButtonOk_Click (ByVal sender as object, ByVal e as eventArgs) Handles ButtonOk.Click 



O "Handler" de um evento aceita sempre dois argumentos:    

1 - Uma referência para o objecto que despoletou o evento. 

2 - Um objecto derivado de "System.EventArgs" que possui informação sobre o próprio evento. 

Exemplo: O "Handler" do Evento KeyPress recebe um objecto do tipo "KeyPressEventArgs", que por sua vez expõe as propriedades "KeyChar" e "Handled": 


  Private Textbox1_KeyPress(ByVal sender as object, ByVal e as KeyPressEventArgs) Handles TextBox1.KeyPress 
If e.KeyChar=" " then e.Handled=true


É possível definir um único "Handler" para tratar eventos provenientes de vários controlos. 

Exemplo: Um único "Handler" para alterar a cor do controlo que ganha o focus: 


  Private Sub Control_Enter (ByVal sender as object, ByVal e as eventArgs) Handles TxtFirstName.Enter, TxtLastName.Enter 
    Dim ctrl as control=Directcast(Sender, Control) 
    ctrl.BackColor=color.red 
End Sub


   


  Private Sub Control_Leave (ByVal sender as object, ByVal e as eventArgs) Handles TxtFirstName.Leave, TxtLastName.Leave 
    Dim ctrl as control=Directcast(Sender, Control) 
    ctrl.BackColor=SystemColors.Window 
End Sub 


Para que um evento de um objecto possa ser processado, utilizando a expressão "Handles", esse objecto teve de ser declarado com a expressão "WithEvents": 


  Public WithEvents FirstName as System.Windows.Forms.TextBox 


Tratamento de Eventos (Run Time) 

Para obter o máximo de flexibilidade no tratamento de eventos, deve utilizar-se o operador "AddHandler". Nomeadamente para que seja possível:    


  Tratar eventos de objectos gerados em "Run Time" 

  Tratar eventos estáticos (Shared) 

  Tratar eventos de colecções e "Arrays" de objectos 



  AddHandler Object.Event, Delegate 


Exemplo: 


  AddHandler TextBox1.Enter, New EventHandler (AddressOf Control_Enter) 


O VB consegue determinar o "Delegate EventHandler" automaticamente, por isso pode ser utilizada a versão simplificada: 


  AddHandler TextBox1.Enter, AddressOf Control_Enter 


Neste exemplo o método "Control_Enter" tem de ser implementado, respeitando a assinatura do "Delegate EventHandler" 

Inversamente, para deixar de receber as notificações de um evento, utiliza-se o operador "RemoveHandler": 


  RemoveHandler TextBox1.Enter, AddressOf Control_Enter 


O operador "removeHandler" deve ser utilizado sempre que tenha sido utilizado previamente o operador "AddHandler", de forma a impedir que os objectos "Delegate" fiquem a consumir recursos após a destruição do objecto. 

Exemplo: 


  Public Class Form1 
    Dim MyButton As New Button 
    Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load 
        MyButton.Text = "Teste" 
        MyButton.Height = 100 
        MyButton.Width = 100 
        Me.Controls.Add(MyButton) 
        AddHandler MyButton.Click, AddressOf ShowMessage 
    End Sub 
    Private Sub ShowMessage(ByVal sender As Object, ByVal e As EventArgs) 
        MsgBox("O botão foi Clicado ") 
        RemoveHandler MyButton.Click, AddressOf ShowMessage 
    End Sub 
End Class 


Exemplo de utilização do operador "AddHandler" para tratar eventos estáticos: 


  Public Class Form1 
    Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load 
        AddHandler Application.Idle, AddressOf Application_Idle 
    End Sub 
    Private Sub Application_Idle(ByVal sender As Object, ByVal e As EventArgs) 
        Label1.Text = "Caracteres: " &amp; TextBox1.TextLength.ToString 
    End Sub 
End Class


O operador "AddHandler" pode ainda ser utilizado para tratar eventos de colecções ou "arrays" objectos. 

Exemplo: 


  Public Class Form1 
    Dim MyButton As Button 
    Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load 
        For x As Integer = 1 To 10 
            MyButton = New Button 
            MyButton.Name = x.ToString 
            MyButton.Text = x.ToString 
            MyButton.Height = 50 
            MyButton.Width = 50 
            MyButton.Left = x * 50 + 10 
            Me.Controls.Add(MyButton) 
            AddHandler MyButton.Click, AddressOf ShowClickedButton 
        Next 
    End Sub 
    Private Sub ShowClickedButton(ByVal sender As Object, ByVal e As EventArgs) 
        Label1.Text = "Clicou no botão:" &amp; CType(sender, Button).Text 
    End Sub 
End Class


Despoletar eventos 

Os eventos, tal como os campos, propriedades e métodos, fazem parte do interface que um tipo expõe aos seus clientes. 

Os eventos são especiais, pois são activados pelo tipo e não pelo cliente. 

Exemplo: 


  Public Event NameChanged (ByVal sender as object, ByVal e as EventArgs) 


Exemplo de uma classe que expõe um evento: 


  Public Class User 
    Public Event NameChanged(ByVal sender As Object, ByVal e As EventArgs) 
    Private _Name As String 
    Public Property Name() As String 
        Get 
            Return _Name 
        End Get 
        Set(ByVal Value As String) 
            If _Name  Value Then 
                _Name = Value 
                RaiseEvent NameChanged(Me, EventArgs.Empty) 
            End If 
        End Set 
    End Property 
End Class


Para tornar o código mais claro e aumentar a performance, deve ser utilizada a forma alternativa, que permite definir um evento em função de um "Delegate" especifico: 


  Public Event NameChanged As EventHandler 


Event Args 

Todos os eventos trabalham com dois argumentos: Uma referência para o objecto que lançou o evento e um objecto do tipo "System.EventArgs" (ou derivado, o nome termina sempre em "EventArgs") que expõe argumentos do evento através de campos ou propriedades. 


  Imports System.ComponentModel 
Public Class User 
    Public Event BeforeLogin As CancelEventHandler 
    Public Event AfterLogin As EventHandler 
    Public Sub Login() 
        Dim e As New CancelEventArgs 
        RaiseEvent BeforeLogin(Me, e) 
        If e.Cancel Then Exit Sub 
        'Código para implementar o Login 
        RaiseEvent AfterLogin(Me, EventArgs.Empty) 
    End Sub 
End Class


Derivando de EventArgs 

Quando não existe nenhuma classe derivada de "System.EventArgs" que exponha as propriedades desejadas é necessário criar uma classe personalizada: 


  Imports System.componentmodel 
    Public Class NameChangingEventArgs 
        Inherits CancelEventArgs 
        Private _ProposedValue As String 
        Public Sub New(ByVal ProposedValue As String) 
            _ProposedValue = ProposedValue 
        End Sub 
        Public ReadOnly Property ProposedValue() As String 
            Get 
                Return _ProposedValue 
            End Get 
    End Property 
End Class


Caso prático 

O objectivo é criar um modelo realista do funcionamento de um mecanismo de extracção de bolas para o jogo "EuroMilhões", utilizando as técnicas estudadas para manipulação de objectos e eventos. 

A lógica da aplicação é a seguinte:    


  Cada bola possui um relógio interno que simula o tempo em segundos que a bola demora a passar junto a porta de saída. 

  Cada vez que a bola passar junto a porta, lança um evento indicando que esta pronta a sair. 

  A tômbola possui um mecanismo de abertura da porta, também dependente de um relógio interno. 

  Ao ser iniciado um jogo, a tômbola procede a criação do número de bolas necessário, subscrevendo os seus eventos. 

  Quando for recebido um evento de que uma bola esta pronta a sair, esta deve ser retirada, apenas se a porta estiver aberta. 

  A porta da tômbola deve ser mantida fechada enquanto esta a ser processada uma bola. 

  Quando a tômbola terminar de extrair todas as bolas necessárias, deve emitir um evento indicando que o jogo terminou. 


Regras para desenvolvimento 


  Criar um novo projecto com o template "Windows Application" 

  
    Criar uma classe "bola" com as seguintes propriedades 

    
      Valor numérico da bola 

      Cor (respeitando as regras do LAB 2) 

      Ciclo (propriedade só de leitura, indicando o ciclo da bola em segundos, ou seja, de quanto em quanto tempo passa pela porta da tômbola) 
    
  

  
    O construtor da classe bola aceita como parâmetro o valor e procede aos seguintes passos: 

    
      Atribui uma cor (pelas regras já definidas) 

      Define aleatoriamente o ciclo da bola 
    
  

  A classe bola deve expor o evento "QueroSair" sempre que atingir o seu ciclo. 

  
    Criar uma classe "Tômbola" com as seguintes propriedades: 

    
      Chave (Propriedade só de leitura que contem a chave encontrada, através de um "array" de inteiros. A propriedade não é indexada.) 
    
  

  
    O construtor da classe "Tômbola" deve aceitar como argumentos: 

    
      A quantidade de bolas necessárias para o jogo 

      A quantidade de bolas a extrair 
    
  

  
    O construtor da classe tômbola deverá ainda realizar os passos: 

    
      Criar as bolas necessárias, subscrevendo os seus eventos 

      Iniciar o relógio que controla a abertura da porta 
    
  

  
    Sempre que a classe "Tômbola" receber um pedido de saída de uma bola, deverá: 

    
      Aceitar a saída caso a porta esteja aberta 

      Guardar o valor da bola 

      Destruir a bola (e deixar de receber os seus eventos) 

      Determinar se foram retiradas todas as bolas necessárias, e se sim, destruir as restantes bolas e fazer o "Raise" de um evento "Fimdejogo" 
    
  

  No form, criar duas instâncias do objecto "tômbola" 

  
    Ao receber a notificação de ambas as tômbolas de que o jogo terminou, apresentar a chave no ecrã. 

    Solução 

    Download:Eventos-Solucao.zip

       &lt;img src="http://feeds.feedburner.com/~r/AnkaanetArtigos/~4/uokLd44B4q0" height="1" width="1"/&gt;</description><dc:creator>Migasp</dc:creator><pubDate>Thu, 19 Jun 2008 23:22:02 GMT</pubDate><guid isPermaLink="false">f1397696-738c-4295-afcd-943feb885714:16</guid><feedburner:origLink>http://www.ankaa.net/Artigos/tabid/114/articleType/ArticleView/articleId/16/Tratamento-de-eventos-e-quotDelegatesquot.aspx</feedburner:origLink></item><item><comments>http://www.ankaa.net/Artigos/tabid/114/articleType/ArticleView/articleId/14/Default.aspx#Comments</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.ankaa.net/DesktopModules/DnnForge%20-%20NewsArticles/RssComments.aspx?TabID=114&amp;ModuleID=491&amp;ArticleID=14</wfw:commentRss><trackback:ping>http://www.ankaa.net/DesktopModules/DnnForge%20-%20NewsArticles/Tracking/Trackback.aspx?ArticleID=14&amp;PortalID=0&amp;TabID=114</trackback:ping><title>Introdu&amp;ccedil;&amp;atilde;o ao desenvolvimento orientado a objectos</title><link>http://feedproxy.google.com/~r/AnkaanetArtigos/~3/YNJv157OgEs/Default.aspx</link><description>Introdução   Neste artigo serão apresentados os conceitos básicos de OOP (Desenvolvimento Orientado a Objectos). No final será apresentado um exemplo prático de um simulador de extracção de bolas do "Euromilhões" com todo o código fonte em Visual Basic .Net.   As classes são templates para objectos            As classes definem os membros de um objecto, definem o seu comportamento e atribuem os seus valores iniciais quando apropriado. Quando uma classe é iniciada é criada em memória uma instância. Esta instância é chamada "Objecto".   Objectos e Membros          Os objectos são compostos por membros, que representam a sua estrutura e funcionalidade. Membros são:         Campos e Propriedades: São membros de informação sobre o objecto.     Métodos: São acções que o objecto pode realizar.     Eventos: São notificações que o objecto recebe ou envia para outros objectos quando acontece uma actividade.       Num cenário baseado na vida real, podemos considerar um objecto carro. Este objecto tem propriedades tais como: Cor, Marca, Modelo, Idade, etc. Esta informação descreve o estado do objecto.     O objecto carro tem também métodos, como: Acelerar, trocar mudança ou virar. Os métodos representam comportamentos que o objecto pode executar.     Eventos representam notificações. Por exemplo, no caso do objecto carro pode ser por exemplo o sobre aquecimento do motor ou evento "Batida" ao interagir com um objecto árvore.       Modelo de objectos   Os objectos mais simples podem consistir somente em algumas propriedades, alguns métodos e até talvez um ou dois eventos, mas objectos complexos podem requerer imensos membros e objectos subordinados. Os objectos podem expor outros objectos como membros .   O modelo de Objectos define a hierarquia dos objectos filhos que compõem a estrutura de um objecto.      Voltando ao exemplo do objecto carro, um carro é um único objecto, mas contém outros objectos subordinados, como por exemplo um objecto motor, quatro objectos rodas, etc.    A composição destes objectos subordinados afecta directamente o funcionamento do carro como um todo.   Encapsulamento          Encapsulamento é o conceito de que a implementação de um objecto é independente do seu interface. Ou seja, uma aplicação interage com um objecto através do seu interface, que consiste nas suas propriedades públicas e métodos. Enquanto este interface permanecer constante a aplicação pode interagir com o componente, mesmo que a sua implementação interna seja totalmente reformulada.   Os objectos apenas devem interagir uns com os outros através dos seus métodos e propriedades públicas. Isto significa que os objectos devem conter em si toda a informação e todos os mecanismos para a manipular. A informação e mecanismos internos do objecto nunca devem ser expostos através do interface.      Se um objecto carro interagir com um objecto condutor, o interface deverá consistir em membros como: método avançar, método recuar, método parar, etc. Esta é toda a informação que o objecto condutor necessita para interagir com o objecto carro.     O objecto carro contem um objecto motor, mas o objecto condutor não tem que saber sequer da sua existência.     Se o objecto motor for trocado, o objecto condutor continuará a interagir com o objecto carro exactamente da mesma forma.    Polimorfismo      Polimorfismo é a capacidade de diferentes classes disponibilizarem implementações diferentes dos mesmos interfaces públicos. Ou seja, o polimorfismo permite que métodos e propriedades de um objecto possam ser chamados, sem conhecimento particular da sua implementação.   O polimorfismo pode ser providenciado de duas formas:         Polimorfismo de interface     Polimorfismo de herança    Um objecto condutor pode interagir com um objecto carro através do seu interface público.   Se outro objecto, como camião ou tractor disponibilizar o mesmo interface, o objecto condutor pode interagir com eles sem se preocupar com a implementação especifica.   Polimorfismo de interface      Um interface é um contrato de comportamento. Essencialmente define quais os membros que a classe vai implementar, mas não diz nada acerca dos detalhes da implementação. Um objecto pode implementar vários interfaces diferentes , e várias classes diferentes podem implementar o mesmo interface.   Todos os objectos que implementam um mesmo interface são capazes de interagir com outros objectos através desse interface.      Utilizando o exemplo anterior, o objecto carro pode implementar o interface "IDrivable" que especifica os métodos avançar e recuar. Outras classes, como camião podem implementar também este interface e assim ser capazes de interagir com o objecto condutor.    Polimorfismo de herança      A herança permite incorporar a funcionalidade previamente definida numa classe numa nova classe, e implementar novos membros se necessário.   Uma classe que herda de outra classe é designada como classe derivada. Uma classe apenas pode herdar directamente de uma classe (classe base).   A nova classe tem os mesmos membros que a classe base, embora possa adicionar ou alterar o funcionamento dos existentes.      Se a classe carro for uma classe base, uma classe derivada pode ser o CarroDesportivo    Métodos   Os métodos podem ser implementados como:       Procedimentos ("Sub")     Funções ("Function")         Function CompleteName(ByRef FirstName as string, ByVal LastName as string, Optional ByVal title As String = "") As String 
    CompleteName = "" 
    ' Use the title if provided. 
    If title  "" Then CompleteName = title &amp; " " 
    ' Append first and last name. 
    CompleteName &amp;= FirstName &amp; " " &amp; LastName 
End Function 


Overloading 

"Overloading" é a possibilidade de criar vários métodos com o mesmo nome mas com assinaturas diferentes. 
  

  A assinatura do método é composta pelos tipos dos seus parâmetros, e pela forma como são passados (ByRef ou ByVal). 

  

  O Retorno do método não é relevante para a assinatura (não pode ser utilizado para "Overloading") 

  


  Function Item (ByVal index as Integer) as String 
    'Acede a elementos através do index 
End Function 



  Function Item (ByVal Key as string) as String 
    'Acede aos elementos através de uma chave "String" 
End Function


 


  'Chamada através do primeiro método 
Result=MyObj.Item(1) 

'Chamada através do segundo método
Result=MyObj.Item("teste")


Regras para "Overloading" 


  A palavra reservada "Overloads" pode ser utilizada de forma opcional. Mas se for utilizada num método tem de ser utilizada em todos os métodos da classe. 

  A utilização de "overloading" é facilitada com as capacidades de "intellisence" do Visual Studio. 

  O "Overloading" pode ser uma alternativa melhor para casos em que se utilizem parâmetros opcionais. 

  Quando os tipos dos argumentos não correspondem exactamente à assinatura de nenhum método disponível, o compilador tenta encontrar um método através de "Widening coercion" 

  Casos ambíguos: 



  A plataforma tem de conseguir resolver um método no momento da compilação, por isso os métodos tem de ter mais alguma diferença para além de parâmetros opcionais. 


Como não é possível distinguir dois métodos apenas pelo tipo de retorno, não é permitido o "overloading" de métodos sem parâmetros. 

Propriedades 

As propriedades podem ser consideradas "Super Fields", pois são manipuladas como os "fields", mas tem código associado. 


  Public Property BirthDate() as date 
    'Código que implementa a propriedade 
End Property


Tal como os campos e os métodos, as propriedades podem possuir um identificador de "Scope". Por defeito as propriedades são publicas. 

Estrutura das propriedades: 

Get … End Get -&gt; Define o valor devolvido pela propriedade 

Set … End Set -&gt; Define como são assignados valores à propriedade 


  Dim m_BirthDate As Date 
'...
Public Property BirthDate() As Date
    Get 
        Return m_BirthDate 
    End Get
    Set(ByVal value As Date) 
        m_BirthDate = value 
    End Set
End Property


A partir do Visual Basic 2005 é possível atribuir definições de "Scope" diferentes aos blocos "Set" e "Get", Esta capacidade é importante, por exemplo, quando se pretende que uma propriedade seja só de leitura se chamada fora do "Assembly" e de escrita se chamada de outras classes dentro do mesmo "Assembly": 


  Private m_ID As Integer 
Public Property ID() As Integer 
    Get 
        Return m_ID 
    End Get
    Friend Set(ByVal value As Integer) 
        If value Then Throw New ArgumentException("Valores negativos são inválidos") 
        m_ID = value 
    End Set
End Property


Propriedades com argumentos 


  Dim m_Addresses(3) As String ' Até 4 linhas 
Public Property Addresses(ByVal index As Integer) As String
    Get 
        If index Or index &gt; UBound(m_Addresses) Then 
            Throw New IndexOutOfRangeException("Indice inválido.") 
        End If 
        Return m_Addresses(index) 
    End Get 
    Set(ByVal value As String) 
        If index Or index &gt; UBound(m_Addresses) Then 
            Throw New IndexOutOfRangeException("Indice inválido.") 
        End If 
        m_Addresses(index) = value 
    End Set 
End Property


Propriedades "Default" 

Um tipo pode expor uma propriedade por defeito, para que possa ser chamada sem que seja necessário utilizar o seu nome, desde que seja uma propriedade com argumentos: 


  Default Public Property Addresses(ByVal index As Integer) As String 
'(…) 
End Property



  'Utilizar a propriedade:
Dim MyPerson as new person
MyPerson.name="Ana"
MyPerson(0)="Avenida da casa amarela" 
MyPerson(1)="Nº149 – Lisboa"


Construtores 

Um construtor é um método especial que é executado sempre que é criada uma instância de uma classe. Em VB o construtor é sempre chamado "Sub New": 


  Class Person 
    Private CreateTime as date 
    Public Sub New() 
        ' Display a diagnostic message. 
        Console.WriteLine("A new instance of Person is being created.") 
        ' Remember when this instance was created. 
        CreateTime = Now 
    End Sub 
End Class


Quando a execução chega ao construtor, todos os campos com um iniciador já foram iniciados: 


  Class Person 
    Public Pais as string="Portugal" 
    Public Sub New()
        Console.writeline(Pais) '-&gt;Portugal 
    End Sub 
End Class


Um construtor pode aceitar argumentos, normalmente para obrigar à definição de valores indispensáveis para a criação e funcionamento correcto do objecto: 


  Class Person 
    Public FirstName as String
    Public LastName as String 
    Public Sub New(ByVal FirstName as String, ByVal LastName as String)
        Me.FirstName=FirstName
        Me.LastName=LastName 
    End Sub 
End Class



  'Instanciação
Dim MyPerson as new Person ("João","Teixeira") 


Construtores "OverLoad" 

Tal como todos os restantes métodos, o "sub New" pode ser "Overloaded", o que significa que podem ser dadas várias opções para a criação de um objecto. 


  Public Sub New(ByVal name As String) 
    ' chama o outro construtor, passando o default do segundo argumento . 
    Me.New(name, 0) 
End Sub 

Public Sub New(ByVal name As String, ByVal id As Integer) 
    Me.Name = name 
    Me.Id = id 
End Sub


Membros estáticos ("Shared"): 

São membros (Campos, Propriedades ou métodos) que pertencem à classe e não à instância. 

Através da palavra reservada "Shared" um método fica marcado como estático, podendo ser chamado sem ser necessário criar uma instância do objecto: 


  Public Class Triangle 
    Dim a, b, c As Double 
    Public Shared Function GetPerimeter(ByVal a As Double, ByVal b As Double, ByVal c As Double) As Double
        Return a + b + c 
    End Function 
    Public Shared Function GetArea(ByVal a As Double, ByVal b As Double, ByVal c As Double) As Double 
        Dim halfP As Double = (a + b + c) / 2 
        Return Math.Sqrt(halfP * (halfP - a) * (halfP - b) * (halfP - c)) 
    End Function 
End class


Regras para a implementação de membros estáticos 


  O Código incluído dentro de um método estático ("Shared") apenas pode aceder a outros membros estáticos. Não pode aceder a campos, propriedades ou métodos de instância. 
      

  Normalmente são criados dois tipos de classes que só contém membros estáticos:   



  
    Uma classe que funciona como um contentor biblioteca de membros estáticos, como por exemplo o "System.Math". 
        

    
      Um tipo que apenas assume a forma de um objecto único (uma única instância) , como por exemplo o "System.Console" (São chamadas Singletons") . 
    
  


Se uma classe apenas contém membros estáticos, não é necessário proceder à sua instanciação. 

Para garantir que uma classe não é instanciada de todo, deve ser criado um construtor privado, para evitar que o VB construa um automaticamente: 


  Public Class Mylibrary 
    Private Sub New 
        'Nada 
    End Sub 
End class 


Uma propriedade identificada como "Shared" 


  Public Shared ReadOnly Property NextInvoiceID() As Long 
    Get 
        Return InstanceCount + 1 
    End Get 
End Property


Construtores estáticos ("Shared") 


  Um construtor "Shared" e sem parâmetros é chamado automaticamente antes da primeira instância da classe ser criada ("Static Constructor" ou "Type constructor"). 

  Um construtor estático é tipicamente utilizado para iniciar de forma correcta campos estáticos, quando é necessária uma lógica mais elaborada que um simples iniciador. 

  Um construtor estático pode ser utilizado para aceder a recursos partilhados por todas as instâncias de um objecto, como por exemplo, um ficheiro. 

  O construtor estático é executado antes do construtor de instância para a primeira instância. 

  Um construtor estático é implicitamente Privado. 

  O construtor estático é o único local onde é possível assignar um valor a um campo "Shared" e "ReadOnly". 



  Public Shared ReadOnly InitialDir as String 
Shared Sub New 
    InitialDir=Directory.GetCurrentDirectory() 
End Sub


Caso prático 

Esta pequena aplicação pretende aplicar na prática os conhecimentos adquiridos em desenvolvimento orientado a objectos. 

O objectivo será criar um modelo realista do funcionamento de um mecanismo de extracção de bolas para o jogo "EuroMilhões". 

A aplicação, ao ser executada, deverá apresentar uma chave (5 números entre 50 mais 2 números entre 9), bem como a seguinte informação:    


  Listagem de todas as bolas que saíram na tômbola grande (incluindo valor e cor) 

  Listagem de todas as bolas que saíram na tômbola pequena (incluindo valor e cor) 

  Listagem de todas as bolas que ficaram na tômbola grande (incluindo valor e cor) 

  Listagem de todas as bolas que ficaram na tômbola pequena (incluindo valor e cor)    


Regras para desenvolvimento    


  Criar um novo projecto com o template "Console Application" 

  
    Criar uma estrutura "Bola" com as seguintes características: 

    
      Propriedade "valor" do tipo "Integer" (deverá ser preenchida com um valor recebido no construtor) 

      Propriedade "Cor" do tipo "String" (Deverá ser preenchida no construtor, através da formula: valor entre 1 e 10 = Amarela, entre 11 e 20 = Vermelha, entre 21 e 30 = Azul, entre 31 e 40 = Verde, entre 41 e 50 = Laranja. 
    
  

  
    Criar uma classe "Tômbola" com as seguintes características: 

    
      Propriedade do tipo "Integer" chamada "Capacidade" (Capacidade máxima da tômbola) 

      Propriedade só de leitura do tipo "integer" chamada "Capacidade ocupada" (Com a capacidade actualmente ocupada da tômbola. Deve ser actualizada sempre que o conteúdo da tômbola seja alterado). 

      Propriedade só de leitura do tipo "Bola" com argumentos por índice, chamada "ConteúdoTombola" (Através de um índice, retorna a bola correspondente existente na tômbola). 

      Método "enche", sem retornos. Recebe um "array" de bolas e coloca no conteúdo na tômbola. 

      Método "BaralhaBolas", sem retornos. Baralha o conteúdo da tômbola (Implementar com um método privado chamado "BaralhaBolasArray". 

      Método "ExtraiBola", que retira uma bola da tômbola e devolve esse objecto. (Implementar com um método privado chamado "RemoveBolaArray". 
    
  

  
    No modulo da "console Application" realizar os seguintes passos: 

    
      Criar 50 instâncias de objectos "Bola" (Definir o valor no momento de construção. Os valores deverão ser de 1 a 50) 

      Criar um objecto do tipo "Tômbola" chamado "TombolaGrande" e definir a sua capacidade. 

      Colocar as bolas dentro da tômbola (método "Enche") 

      Baralhar a tômbola (Método "BaralhaBolas") 

      Retirar 5 bolas da tômbola (Método "ExtraiBola". A tômbola deverá ser baralhada entre cada saída de uma bola). 

      Repetir os passos, com 9 bolas e um objecto "Tômbola" chamado "TombolaPequena" 

      Apresentar no ecrã a informação pedida. 
    

    Solução 

    Download:Eventos-Solucao.zip
    
      

         &lt;img src="http://feeds.feedburner.com/~r/AnkaanetArtigos/~4/YNJv157OgEs" height="1" width="1"/&gt;</description><dc:creator>Migasp</dc:creator><pubDate>Thu, 19 Jun 2008 18:45:09 GMT</pubDate><guid isPermaLink="false">f1397696-738c-4295-afcd-943feb885714:14</guid><feedburner:origLink>http://www.ankaa.net/Artigos/tabid/114/articleType/ArticleView/articleId/14/Default.aspx</feedburner:origLink></item><item><comments>http://www.ankaa.net/Artigos/tabid/114/articleType/ArticleView/articleId/13/Default.aspx#Comments</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.ankaa.net/DesktopModules/DnnForge%20-%20NewsArticles/RssComments.aspx?TabID=114&amp;ModuleID=491&amp;ArticleID=13</wfw:commentRss><trackback:ping>http://www.ankaa.net/DesktopModules/DnnForge%20-%20NewsArticles/Tracking/Trackback.aspx?ArticleID=13&amp;PortalID=0&amp;TabID=114</trackback:ping><title>Usar o c&amp;oacute;digo fonte do m&amp;oacute;dulo &amp;quot;Blog&amp;quot; do Dotnetnuke convertendo-o de DAL para DAL+</title><link>http://feedproxy.google.com/~r/AnkaanetArtigos/~3/S_LYXCnWrZ4/Default.aspx</link><description>Recentemente tive necessidade de fazer um projecto em que a funcionalidade de blog tinha de ser bastante personalizada. Decidi utilizar a última versão do modulo Blog do DotNetNuke, e a partir de ai fazer as alterações necessárias.        Este foi precisamente o primeiro desafio, a  utilização do pacote "source" do módulo blog não foi nada trivial. Para começar o módulo de Blog foi desenvolvido para a framework .net 1.1. A tentativa de instalação deste pacote numa versão anterior (3.x) do DNN e sua posterior migração para DNN v4.x através do "Module Upgrade Wizard" resultou sempre em erros de resolução extremamente complicada. Mas para mim mesmo a simples utilização deste pacote num site DNN 3.x com a framework 1.1 se revelou impraticável.         Optei por alterar o código original para DAL+ uma vez que com este processo, a personalização do módulo se torna mais simples. Isto só é possível pois apenas pretendo utilizar este módulo em bases de dados SQL. Mais informações sobre a escolha entre DAL ou DAL+ pode ser encontrada no artigo do Michael Washington.         Uma vez que vamos obter um novo módulo de blog, diferente do original, é recomendado que lhe demos um novo nome. Desta forma poderemos ter os dois módulos a conviver pacificamente no nosso projecto. Neste exemplo o nome Blog é alterado para Ankaa_Blog.         Este artigo pretende relatar exactamente os passos que segui para conseguir utilizar e compilar o código fonte deste módulo, deixando-o preparado para posterior personalização. Os passos descritos devem ser semelhantes para a migração de outros módulos.         O primeiro passo é precisamente fazer download da última versão do pacote de código que é composto por um ficheiro zip, em cujo interior existe um segundo ficheiro ZIP chamado "DNN_Blog_Resources.zip". É este segundo ficheiro que necessitamos descomprimir  para um directório no disco.         De seguida é necessário criar o nosso projecto, o que pode ser feito através do "starter kit" ou através do "Source Package".     Após criar o projecto, deve aceder à sua página e proceder à instalação normal da base de dados.         Nesta fase temos um projecto DNN a funcionar correctamente e um directório com o código fonte do módulo blog. Podemos começar as tarefas para integrar o código fonte do blog no nosso projecto:         Criar a base de dados e "stored procedures"         O primeiro passo corresponde à importação de todos os elementos referentes à base de dados que suporta este módulo. Tal como descrito anteriormente, a migração deste módulo é feita alterando o nome do módulo para que seja possível a sua convivência pacifica com o módulo original. Para isso iremos alterar os nomes de todos os objectos de dados para um nome alternativo. Nesta caso será utilizado o nome "Ankaa_Blog" podendo ser utilizador qualquer outro.  1 - Alterar os nomes de todos os objectos de dados para o nome alternativo. Os ficheiros com os scripts de SQL que devemos utilizar para construir a base de dados estão dentro da pasta em que descomprimimos os ficheiros de código do blog original, no caminho "\Providers\DataProviders\SqlDataProvider". Os ficheiros em causa são os "xx.xx.xx.SqlDataProvider", em que os xx representam os valores da versão.  Retirar o atributo "ReadOnly"do ficheiros e dentro de cada um destes ficheiros substituir o texto "{objectQualifier}Blog" por "{objectQualifier}Ankaa_Blog"  No ficheiro "03.03.00.SqlDataProvider" alterar  a linha 64 de:  "where {databaseOwner}{objectQualifier}DesktopModules.ModuleName = 'Blog' AND {databaseOwner}{objectQualifier}Modules.IsDeleted = 0"  Para:  "where {databaseOwner}{objectQualifier}DesktopModules.ModuleName = 'Ankaa_Blog' AND {databaseOwner}{objectQualifier}Modules.IsDeleted = 0"  2 -Importar os ficheiros SQL. Para isso devemos lançar o site  a partir do Visual Studio (Ctrl + F5) e entrar com a conta "Host".  Uma vez dentro do portal, escolher a opção "SQL" do menu "Host"           Seleccionar a opção "Run as Script", e ler cada um dos ficheiros alterados anteriormente ("Browse", escolher o ficheiro na pasta onde foram descomprimidos,  e escolher a opção "Load").  Atenção: Os ficheiros tem de ser carregados por ordem de versão. Para a versão actual do módulo (03.03.00), devem ser carregados na seguinte ordem:           Para cada ficheiro, escolher a opção "Execute" (Atenção: Garantir que a opção "Run as Script" está seleccionada).  Se ocorrer um erro na importação de algum dos ficheiros, abrir o ficheiro em causa e processar cada parcela do ficheiro individualmente. Para isso copiar todas as linhas entre cada instrução "GO" (sem o "GO") e colocar na caixa de SQL do portal (com a opção "Run as script activa") e clicar em "Execute". Algum erro reportado nesta fase, será provavelmente devido a instruções que foram executadas antes do erro anterior, e poderão ser ignoradas.  Adicionar o código "Business Logic"  1 - No "Visual Studio" Criar uma pasta chamada "Ankaa_Blog" dentro da pasta "App_Code".  2 - Clicar com o botão direito sobre o directório agora criado e escolher a opção "Add existing item"           3 - Adicionar os ficheiros correspondentes à lógica de negócio. A partir da pasta onde estão os ficheiros de código do módulo "Blog" Navegar até à pasta "Components" e escolher todos os ficheiros ai existentes.           4 - Clicar com o botão direito sobre a pasta "Ankaa_Blog" e adicionar duas novas pastas chamadas "Rss" e "Upgrade".  5 - Clicar com o botão direito sobre a pasta "Rss", escolher a opção "Add existing item". A partir da pasta onde estão os ficheiros de código do módulo "Blog" Navegar até à pasta "Rss" e escolher todos os ficheiros ai existentes.  6 - Repetir o processo para a pasta "Upgrade", importando os ficheiros de código da pasta "Upgrade"           Alterar o código da "Business Logic" para os nomes correctos  1 - O primeiro passo é alterar os nomes dos objectos e dos namespaces de forma a obtermos um novo módulo independente do módulo "Blog" original.  Para isso, em cada um dos ficheiros importados (garantir que a opção "Search Hidden Text" está seleccionada) alterar todas as referências do texto:  "DotNetNuke.Modules.Blog"  Para  "Ankaa.Modules.Blog"  Corrigir referências a NameSpaces core  Os passos seguintes descrevem as correcções para a versão 03.03.00 do módulo "Blog". Em versões futuras deste módulo as correcções poderão ser diferentes.  1 - Fazer "Build Solution" de forma a identificar as referências incorrectas que necessitam correcção. Neste momento são identificados dois erros na solução:           2 - Os dois erros são corrigidos adicionando  a linha "Imports DotNetNuke"  ao ficheiro "InterfaceController.vb".  Alterar o código da "Business Logic" para DAL+ e para os nomes correctos  A alteração da lógica de negócio de DAL para DAL+ tem de ser feita de forma individual e cuidada. O objectivo é substituir código semelhante a  "DataProvider.Instance().GetBlogMonths(PortalID, BlogID)"  Por código semelhante a  "DotNetNuke.Data.DataProvider.Instance().ExecuteReader("Menkar_Blog_GetBlogMonths", PortalID, BlogID)"  Dividindo a versão original em partes, e fazendo a correspondência com a versão DAL+:                                       Versão Original (DAL)                              Versão Nova (DAL+)                              Explicação                                            DataProvider.Instance()                              DotNetNuke.Data.DataProvider.Instance()                              Acesso ao Data Provider                                            GetBlogMonths(PortalID, BlogID)                              ExecuteReader("Menkar_Blog_GetBlogMonths", PortalID, BlogID)                              Utilização de uma Stored Procedure com passagem de parametros, neste caso o PortalID e o BlogID.                         Enquanto que na versão original a chamada à stored procedure é directa, na versão nova é necessário utilizar um dos 4 métodos da DAL+. É assim necessário para cada caso é determinar qual dos métodos é o adequado:                                        ExecuteNonQuery                              Utilizado quando se pretende executar uma stored procedure que não retorna nenhum valor.                                            ExecuteReader                              Utilizado para executar uma stored procedure que retorne multiplos registos.                                            ExecuteScalar                              Utilizado para executar uma stored procedure que retorna apenas um valor .                                            ExecuteSQL                              Utilizado para executar um comando SQL directamente.                         Para cada caso individual é necessário determinar o método correcto da DAL+ a utilizar bem como a Stored Procedure a ser utilizada. A lista indicada a seguir corresponde à versão 03.03.00 do módulo blog, e poderá ser diferente em futuras versões deste módulo:         1 - Procurar em todos os ficheiros  importados  (a opção "Search hidden text" deve estar activa) o texto "DataProvider.Instance()." em cada caso fazer a substituição correcta:                                        Ficheiro                              Linha                              Texto Original                              Texto Novo                                            ArchiveController.vb                              30                              Return CBO.FillCollection(DataProvider.Instance().GetBlogMonths(PortalID, BlogID), GetType(ArchiveMonths))                              Return CBO.FillCollection(DotNetNuke.Data.DataProvider.Instance().ExecuteReader("Ankaa_Blog_GetBlogMonths", PortalID, BlogID), GetType(ArchiveMonths))                                                                           35                              Return CBO.FillCollection(DataProvider.Instance().GetBlogDaysForMonth(PortalID, BlogID, BlogDate), GetType(ArchiveDays))                              Return CBO.FillCollection(DotNetNuke.Data.DataProvider.Instance().ExecuteReader("Ankaa_Blog_GetBlogDaysForMonth", PortalID, BlogID, BlogDate), GetType(ArchiveDays))                                            UpgradeController.vb                              32                              Return CBO.FillCollection(DataProvider.Instance().Upgrade_ListBlogs(PortalID, ParentBlogID, ShowNonPublic), GetType(BlogInfo))                              Return CBO.FillCollection(DotNetNuke.Data.DataProvider.Instance().ExecuteReader("Ankaa_Blog_Upgrade_ListBlogs", PortalID, ParentBlogID, ShowNonPublic), GetType(BlogInfo))                                                                           38                              DataProvider.Instance().Upgrade_DeleteBlog(blogID)                              DotNetNuke.Data.DataProvider.Instance().ExecuteReader("Ankaa_Blog_Upgrade_DeleteBlog", blogID)                                                                           44                              Return CBO.FillCollection(DataProvider.Instance().Upgrade_ListEntriesByBlog(BlogID, BlogDate, ShowNonPublic, ShowNonPublished), GetType(EntryInfo))                              Return CBO.FillCollection(DotNetNuke.Data.DataProvider.Instance().ExecuteReader("Ankaa_Blog_Upgrade_ListEntriesByBlog", BlogID, BlogDate, ShowNonPublic, ShowNonPublished), GetType(EntryInfo))                                                                           50                              DataProvider.Instance().Upgrade_DeleteEntry(EntryID)                              DotNetNuke.Data.DataProvider.Instance().ExecuteReader("Ankaa_Blog_Upgrade_DeleteEntry", EntryID)                                                                           56                              Return CBO.FillCollection(DataProvider.Instance().Upgrade_ListComments(EntryID), GetType(CommentInfo))                              Return CBO.FillCollection(DotNetNuke.Data.DataProvider.Instance().ExecuteReader("Ankaa_Blog_Upgrade_ListComments", EntryID), GetType(CommentInfo))                                                                           62                              DataProvider.Instance().Upgrade_DeleteComment(commentID)                              DotNetNuke.Data.DataProvider.Instance().ExecuteReader("Ankaa_Blog_Upgrade_DeleteComment", commentID)                                            ForumBlogUpgrade.vb                              221                              Return CBO.FillCollection(DataProvider.Instance().Upgrade_ListForum_Groups(PortalId), GetType(Forum_GroupsInfo))                              Return CBO.FillCollection(DotNetNuke.Data.DataProvider.Instance().ExecuteReader("Ankaa_Blog_Upgrade_ForumGroupsList", PortalId), GetType(Forum_GroupsInfo))                                                                           227                              Return CType(CBO.FillObject(DataProvider.Instance().Upgrade_ListForum_GroupsByGroup(groupID), GetType(Forum_GroupsInfo)), Forum_GroupsInfo)                              Return CType(CBO.FillObject(DotNetNuke.Data.DataProvider.Instance().ExecuteReader("Ankaa_Blog_Upgrade_ForumGroupsByGroup", groupID), GetType(Forum_GroupsInfo)), Forum_GroupsInfo)                                                                           599                              Return CBO.FillCollection(DataProvider.Instance().Upgrade_ListForum_Forums(GroupID), GetType(Forum_ForumsInfo))                              Return CBO.FillCollection(DotNetNuke.Data.DataProvider.Instance().ExecuteReader("Ankaa_Blog_Upgrade_ForumForumsList", GroupID), GetType(Forum_ForumsInfo))                                                                           749                              Return CBO.FillCollection(DataProvider.Instance().Upgrade_ListForum_Threads(ForumID), GetType(Forum_ThreadsInfo))                              Return CBO.FillCollection(DotNetNuke.Data.DataProvider.Instance().ExecuteReader("Ankaa_Blog_Upgrade_ForumThreadsList", ForumID), GetType(Forum_ThreadsInfo))                                                                           1021                              Return CBO.FillCollection(DataProvider.Instance().Upgrade_ListForum_Posts(ThreadID), GetType(Forum_PostsInfo))                              Return CBO.FillCollection(DotNetNuke.Data.DataProvider.Instance().ExecuteReader("Ankaa_Blog_Upgrade_ForumPostsList", ThreadID), GetType(Forum_PostsInfo))                                                                           1106                              Return CBO.FillCollection(DataProvider.Instance().Upgrade_ListForum_ThreadRating(ThreadID), GetType(Forum_ThreadRatingInfo))                              Return CBO.FillCollection(DotNetNuke.Data.DataProvider.Instance().ExecuteReader("Ankaa_Blog_Upgrade_ForumThreadRatingList", ThreadID), GetType(Forum_ThreadRatingInfo))                                            Utility.vb                              131                              Dim dr As IDataReader = Ankaa.Modules.Blog.Data.DataProvider.Instance().GetBlogModuleSettings(PortalID, TabID)                              Dim dr As IDataReader = DotNetNuke.Data.DataProvider.Instance().ExecuteReader("Ankaa_Blog_GetSettings", PortalID, TabID)                                                                           156                              Ankaa.Modules.Blog.Data.DataProvider.Instance().UpdateBlogModuleSetting(PortalID, TabID, Key, Value)                              DotNetNuke.Data.DataProvider.Instance().ExecuteReader("Ankaa_Blog_UpdateSetting", PortalID, TabID, Key, Value)                                                                           168                              mid = Ankaa.Modules.Blog.Data.DataProvider.Instance().GetBlogViewEntryModuleID(TabID)                              mid = DotNetNuke.Data.DataProvider.Instance().ExecuteScalar("Ankaa_Blog_GetBlogViewEntryModuleID", TabID)                                                                           315                              ModuleID = Ankaa.Modules.Blog.Data.DataProvider.Instance().GetBlogViewEntryModuleID(TabID)                              ModuleID = DotNetNuke.Data.DataProvider.Instance().ExecuteScalar("Ankaa_Blog_GetBlogViewEntryModuleID", TabID)                                            EntryController.vb                              30                              Return CType(CBO.FillObject(DataProvider.Instance().GetEntry(EntryID), GetType(EntryInfo)), EntryInfo)                              Return CType(CBO.FillObject(DotNetNuke.Data.DataProvider.Instance().ExecuteReader("Ankaa_Blog_GetEntry", EntryID), GetType(EntryInfo)), EntryInfo)                                                                           35                              Return CBO.FillCollection(DataProvider.Instance().ListEntries(PortalID, BlogID, BlogDate, ShowNonPublic, ShowNonPublished), GetType(EntryInfo))                              Return CBO.FillCollection(DotNetNuke.Data.DataProvider.Instance().ExecuteReader("Ankaa_Blog_ListEntries", PortalID, BlogID, BlogDate, ShowNonPublic, ShowNonPublished), GetType(EntryInfo))                                                                           39                              Return CBO.FillCollection(DataProvider.Instance().ListEntriesByBlog(BlogID, BlogDate, ShowNonPublic, ShowNonPublished, RecentEntriesMax), GetType(EntryInfo))                              Return CBO.FillCollection(DotNetNuke.Data.DataProvider.Instance().ExecuteReader("Ankaa_Blog_ListEntriesByBlog", BlogID, BlogDate, ShowNonPublic, ShowNonPublished, RecentEntriesMax), GetType(EntryInfo))                                                                           43                              Return CBO.FillCollection(DataProvider.Instance().ListAllEntriesByBlog(BlogID), GetType(EntryInfo))                              Return CBO.FillCollection(DotNetNuke.Data.DataProvider.Instance().ExecuteReader("Ankaa_Blog_ListAllEntriesByBlog", BlogID), GetType(EntryInfo))                                                                           47                              Return CBO.FillCollection(DataProvider.Instance().ListEntriesByPortal(PortalID, BlogDate, ShowNonPublic, ShowNonPublished, RecentEntriesMax), GetType(EntryInfo))                              Return CBO.FillCollection(DotNetNuke.Data.DataProvider.Instance().ExecuteReader("Ankaa_Blog_ListEntriesByPortal", PortalID, BlogDate, ShowNonPublic, ShowNonPublished, RecentEntriesMax), GetType(EntryInfo))                                                                           51                              Return CBO.FillCollection(DataProvider.Instance().ListAllEntriesByPortal(PortalID, ShowNonPublic, ShowNonPublished), GetType(EntryInfo))                              Return CBO.FillCollection(DotNetNuke.Data.DataProvider.Instance().ExecuteReader("Ankaa_Blog_ListAllEntriesByPortal", PortalID, ShowNonPublic, ShowNonPublished), GetType(EntryInfo))                                                                           55                              Return CType(DataProvider.Instance().AddEntry(objEntry.BlogID, objEntry.Title, objEntry.Description, objEntry.Entry, objEntry.Published, objEntry.AllowComments, objEntry.AddedDate.ToUniversalTime, objEntry.DisplayCopyright, objEntry.Copyright, objEntry.PermaLink), Integer)                              Return CType(DotNetNuke.Data.DataProvider.Instance().ExecuteScalar("Ankaa_Blog_AddEntry", objEntry.BlogID, objEntry.Title, objEntry.Description, objEntry.Entry, objEntry.Published, objEntry.AllowComments, objEntry.AddedDate.ToUniversalTime, objEntry.DisplayCopyright, objEntry.Copyright, objEntry.PermaLink), Integer)                                                                           59                              DataProvider.Instance().UpdateEntry(objEntry.BlogID, objEntry.EntryID, objEntry.Title, objEntry.Description, objEntry.Entry, objEntry.Published, objEntry.AllowComments, objEntry.AddedDate.ToUniversalTime, objEntry.DisplayCopyright, objEntry.Copyright, objEntry.PermaLink)                              DotNetNuke.Data.DataProvider.Instance().ExecuteNonQuery("Ankaa_Blog_UpdateEntry", objEntry.BlogID, objEntry.EntryID, objEntry.Title, objEntry.Description, objEntry.Entry, objEntry.Published, objEntry.AllowComments, objEntry.AddedDate.ToUniversalTime, objEntry.DisplayCopyright, objEntry.Copyright, objEntry.PermaLink)                                                                           64                              DataProvider.Instance().DeleteEntry(EntryID)                              DotNetNuke.Data.DataProvider.Instance().ExecuteNonQuery("Ankaa_Blog_DeleteEntry", EntryID)                                            BlogController.vb                              30                              Return CType(CBO.FillObject(DataProvider.Instance().GetBlog(blogID), GetType(BlogInfo)), BlogInfo)                              Return CType(CBO.FillObject(DotNetNuke.Data.DataProvider.Instance().ExecuteReader("Ankaa_Blog_GetBlog", blogID), GetType(BlogInfo)), BlogInfo)                                                                           36                              Return CType(CBO.FillObject(DataProvider.Instance().GetBlogByUserID(PortalID, UserID), GetType(BlogInfo)), BlogInfo)                              Return CType(CBO.FillObject(DotNetNuke.Data.DataProvider.Instance().ExecuteReader("Ankaa_Blog_GetBlogByUserID", PortalID, UserID), GetType(BlogInfo)), BlogInfo)                                                                           42                              Return CType(CBO.FillObject(DataProvider.Instance().GetBlogByUserName(PortalID, UserName), GetType(BlogInfo)), BlogInfo)                              Return CType(CBO.FillObject(DotNetNuke.Data.DataProvider.Instance().ExecuteReader("Ankaa_Blog_GetBlogByUserName", PortalID, UserName), GetType(BlogInfo)), BlogInfo)                                                                           48                              Return CBO.FillCollection(DataProvider.Instance().ListBlogs(PortalID, ParentBlogID, ShowNonPublic), GetType(BlogInfo))                              Return CBO.FillCollection(DotNetNuke.Data.DataProvider.Instance().ExecuteReader("Ankaa_Blog_ListBlogs", PortalID, ParentBlogID, ShowNonPublic), GetType(BlogInfo))                                                                           54                              Return CBO.FillCollection(DataProvider.Instance().ListBlogsByPortal(PortalID, ShowNonPublic), GetType(BlogInfo))                              Return CBO.FillCollection(DotNetNuke.Data.DataProvider.Instance().ExecuteReader("Ankaa_Blog_ListBlogsByPortal", PortalID, ShowNonPublic), GetType(BlogInfo))                                                                           60                              Return CBO.FillCollection(DataProvider.Instance().ListBlogsRootByPortal(PortalID), GetType(BlogInfo))                              Return CBO.FillCollection(DotNetNuke.Data.DataProvider.Instance().ExecuteReader("Ankaa_Blog_ListBlogsRootByPortal", PortalID), GetType(BlogInfo))                                                                           66                              Return CType(DataProvider.Instance().AddBlog(objBlog.PortalID, objBlog.ParentBlogID, objBlog.UserID, objBlog.Title, objBlog.Description, objBlog.Public, objBlog.AllowComments, objBlog.AllowAnonymous, objBlog.ShowFullName, objBlog.Culture, objBlog.DateFormat, objBlog.TimeZone, objBlog.Syndicated, objBlog.SyndicateIndependant, objBlog.SyndicationURL, objBlog.SyndicationEmail, objBlog.EmailNotification, objBlog.AllowTrackbacks, objBlog.AutoTrackback, objBlog.MustApproveComments, objBlog.MustApproveAnonymous, objBlog.MustApproveTrackbacks, objBlog.UseCaptcha), Integer)                              Return CType(DotNetNuke.Data.DataProvider.Instance().ExecuteScalar("Ankaa_Blog_AddBlog", objBlog.PortalID, objBlog.ParentBlogID, objBlog.UserID, objBlog.Title, objBlog.Description, objBlog.Public, objBlog.AllowComments, objBlog.AllowAnonymous, objBlog.ShowFullName, objBlog.Culture, objBlog.DateFormat, objBlog.TimeZone, objBlog.Syndicated, objBlog.SyndicateIndependant, objBlog.SyndicationURL, objBlog.SyndicationEmail, objBlog.EmailNotification, objBlog.AllowTrackbacks, objBlog.AutoTrackback, objBlog.MustApproveComments, objBlog.MustApproveAnonymous, objBlog.MustApproveTrackbacks, objBlog.UseCaptcha), Integer)                                                                           72                              DataProvider.Instance().UpdateBlog(objBlog.PortalID, objBlog.BlogID, objBlog.ParentBlogID, objBlog.UserID, objBlog.Title, objBlog.Description, objBlog.Public, objBlog.AllowComments, objBlog.AllowAnonymous, objBlog.ShowFullName, objBlog.Culture, objBlog.DateFormat, objBlog.TimeZone, objBlog.Syndicated, objBlog.SyndicateIndependant, objBlog.SyndicationURL, objBlog.SyndicationEmail, objBlog.EmailNotification, objBlog.AllowTrackbacks, objBlog.AutoTrackback, objBlog.MustApproveComments, objBlog.MustApproveAnonymous, objBlog.MustApproveTrackbacks, objBlog.UseCaptcha)                              DotNetNuke.Data.DataProvider.Instance().ExecuteNonQuery("Ankaa_Blog_UpdateBlog", objBlog.PortalID, objBlog.BlogID, objBlog.ParentBlogID, objBlog.UserID, objBlog.Title, objBlog.Description, objBlog.Public, objBlog.AllowComments, objBlog.AllowAnonymous, objBlog.ShowFullName, objBlog.Culture, objBlog.DateFormat, objBlog.TimeZone, objBlog.Syndicated, objBlog.SyndicateIndependant, objBlog.SyndicationURL, objBlog.SyndicationEmail, objBlog.EmailNotification, objBlog.AllowTrackbacks, objBlog.AutoTrackback, objBlog.MustApproveComments, objBlog.MustApproveAnonymous, objBlog.MustApproveTrackbacks, objBlog.UseCaptcha)                                                                           78                              DataProvider.Instance().DeleteBlog(blogID)                              DotNetNuke.Data.DataProvider.Instance().ExecuteReader("Ankaa_Blog_DeleteBlog", blogID)                                            SearchController.vb                              29                              Return CBO.FillCollection(DataProvider.Instance().SearchByKeyWordByPortal(PortalID, SearchString, ShowNonPublic, ShowNonPublished), GetType(SearchResult))                              Return CBO.FillCollection(DotNetNuke.Data.DataProvider.Instance().ExecuteReader("Ankaa_Blog_SearchByKeyWordByPortal", PortalID, SearchString, ShowNonPublic, ShowNonPublished), GetType(SearchResult))                                                                           33                              Return CBO.FillCollection(DataProvider.Instance().SearchByKeyWordByBlog(BlogID, SearchString, ShowNonPublic, ShowNonPublished), GetType(SearchResult))                              Return CBO.FillCollection(DotNetNuke.Data.DataProvider.Instance().ExecuteReader("Ankaa_Blog_SearchByKeyWordByBlog", BlogID, SearchString, ShowNonPublic, ShowNonPublished), GetType(SearchResult))                                                                           37                              Return CBO.FillCollection(DataProvider.Instance().SearchByPhraseByPortal(PortalID, SearchString, ShowNonPublic, ShowNonPublished), GetType(SearchResult))                              Return CBO.FillCollection(DotNetNuke.Data.DataProvider.Instance().ExecuteReader("Ankaa_Blog_SearchByPhraseByPortal", PortalID, SearchString, ShowNonPublic, ShowNonPublished), GetType(SearchResult))                                                                           41                              Return CBO.FillCollection(DataProvider.Instance().SearchByPhraseByBlog(BlogID, SearchString, ShowNonPublic, ShowNonPublished), GetType(SearchResult))                              Return CBO.FillCollection(DotNetNuke.Data.DataProvider.Instance().ExecuteReader("Ankaa_Blog_SearchByPhraseByBlog", BlogID, SearchString, ShowNonPublic, ShowNonPublished), GetType(SearchResult))                                            CommentController.vb                              30                              Return CType(CBO.FillObject(DataProvider.Instance().GetComment(commentID), GetType(CommentInfo)), CommentInfo)                              Return CType(CBO.FillObject(DotNetNuke.Data.DataProvider.Instance().ExecuteReader("Ankaa_Blog_GetComment", commentID), GetType(CommentInfo)), CommentInfo)                                                                           36                              Return CBO.FillCollection(DataProvider.Instance().ListComments(EntryID, Approved), GetType(CommentInfo))                              Return CBO.FillCollection(DotNetNuke.Data.DataProvider.Instance().ExecuteReader("Ankaa_Blog_ListComments", EntryID, Approved), GetType(CommentInfo))                                                                           42                              Return CType(DataProvider.Instance().AddComment(objComment.EntryID, objComment.UserID, objComment.Title, objComment.Comment, objComment.Author, objComment.Approved), Integer)                              Return CType(DotNetNuke.Data.DataProvider.Instance().ExecuteScalar("Ankaa_Blog_AddComment", objComment.EntryID, objComment.UserID, objComment.Title, objComment.Comment, objComment.Author, objComment.Approved), Integer)                                                                           48                              DataProvider.Instance().UpdateComment(objComment.CommentID, objComment.EntryID, objComment.UserID, objComment.Title, objComment.Comment, objComment.Author, objComment.Approved)                              DotNetNuke.Data.DataProvider.Instance().ExecuteNonQuery("Ankaa_Blog_UpdateComment", objComment.CommentID, objComment.EntryID, objComment.UserID, objComment.Title, objComment.Comment, objComment.Author, objComment.Approved)                                                                           54                              DataProvider.Instance().DeleteComment(commentID)                              DotNetNuke.Data.DataProvider.Instance().ExecuteNonQuery("Ankaa_Blog_DeleteComment", commentID)                        2 - Fazer o "Build Solution" e confirmar que não existe nenhum erro.        Adicionar o código para o "User Interface"          1 - No "Visual Studio" Criar uma pasta chamada "Ankaa_Blog" dentro da pasta "DesktopModules" (Se esta pasta não existir, cria-la na raiz do site ou lançar o visual studio em modo "Debug" para que seja criada automaticamente).  2 - Clicar com o botão direito sobre o directório agora criado e escolher a opção "Add existing item".  3 - Adicionar os ficheiros correspondentes ao "User Interface".  A partir da pasta onde estão os ficheiros de código do módulo "Blog" adicionar todos os ficheiros  *.ascx, *.vb, *.resx, *.css, *.aspx e *.dnn:           4 - Clicar com o botão direito sobre a pasta "Ankaa_Blog" e adicionar três novas pastas chamadas "Images", "Styles" e "App_LocalResources".  5 - Clicar com o botão direito sobre a pasta "Images", escolher a opção "Add existing item". A partir da pasta onde estão os ficheiros de código do módulo "Blog" Navegar até à pasta "Images" e escolher todos os ficheiros ai existentes.  6 - Repetir o processo para a pasta "Styles", importando os ficheiros de código da pasta "Styles"  7 - Repetir o processo para a pasta "App_LocalResources" importando os ficheiros da pasta "App_LocalResources"           Alterar o código da "User Interface" para os nomes correctos          1 - O primeiro passo é novamente alterar os nomes dos objectos e dos namespaces de forma a obtermos um novo módulo independente do módulo "Blog" original.  Para isso, em todos os ficheiros  que acabámos de importar (garantir que a opção "Search Hidden Text" está seleccionada) alterar todas as referências do texto:  "DotNetNuke.Modules.Blog"  Para  "Ankaa.Modules.Blog"  2 - Em cada um dos ficheiros *.vb acabados de importar alterar todas as referências de texto:  "Public MustInherit Class" ou "Public Class"  Para  "Partial Public Class"  3 - Em cada um destes ficheiros *.vb, procurar e remover todas as linhas incluídas na região "Controls". Estas linhas apenas são necessárias na framework 1.1:           Alterar o código da "User Interface" para DAL+ e para os nomes correctos         Tal como foi feito para a lógica de negócio,  a alteração  de DAL para DAL+ tem de ser feita de forma individual e cuidada. O objectivo é substituir código semelhante a  "DataProvider.Instance().GetBlogMonths(PortalID, BlogID)"  Por código semelhante a  "DotNetNuke.Data.DataProvider.Instance().ExecuteReader("Menkar_Blog_GetBlogMonths", PortalID, BlogID)"  A lista indicada a seguir corresponde à versão 03.03.00 do módulo blog, e poderá ser diferente em futuras versões deste módulo:  1 - Procurar em todos os ficheiros *.vb importados para o "DeskTopModules" (a opção "Search hidden text" deve estar activa) o texto "DataProvider.Instance()." em cada caso fazer a substituição correcta:                                       Ficheiro                              Linha                              Texto Original                              Texto Novo                                            BlogFeed.ascx.vb                              60                              dr = Ankaa.Modules.Blog.Data.DataProvider.Instance().ListEntriesByBlog(ID, Date.UtcNow, Utility.HasBlogPermission(UserId, m_oBlog.UserID, ModuleId), Utility.HasBlogPermission(UserId, m_oBlog.UserID, ModuleId), CType(BlogSettings("RecentRssEntriesMax"), Integer))                              dr = DotNetNuke.Data.DataProvider.Instance().ExecuteReader("Ankaa_Blog_ListEntriesByBlog", ID, Date.UtcNow, Utility.HasBlogPermission(UserId, m_oBlog.UserID, ModuleId), Utility.HasBlogPermission(UserId, m_oBlog.UserID, ModuleId), CType(BlogSettings("RecentRssEntriesMax"), Integer))                                                                           78                              dr = Ankaa.Modules.Blog.Data.DataProvider.Instance().ListEntriesByBlog(ID, m_dBlogDate.ToUniversalTime, Utility.HasBlogPermission(UserId, m_oBlog.UserID, ModuleId), Utility.HasBlogPermission(UserId, m_oBlog.UserID, ModuleId), CType(BlogSettings("RecentRssEntriesMax"), Integer))                              dr = DotNetNuke.Data.DataProvider.Instance().ExecuteReader("Ankaa_Blog_ListEntriesByBlog", ID, m_dBlogDate.ToUniversalTime, Utility.HasBlogPermission(UserId, m_oBlog.UserID, ModuleId), Utility.HasBlogPermission(UserId, m_oBlog.UserID, ModuleId), CType(BlogSettings("RecentRssEntriesMax"), Integer))                        Corrigir Diferenças de ASP1.1 para ASP 2.0            1 - Em cada um dos ficheiros *.ascx e *.aspx  importados para a "User Interface" fazer replace de todas as referências a:  "Codebehind="  Para  "CodeFile="  2 - Acrescentar ao ficheiro "ModuleOptions.ascx.vb" a importação "Imports DotNetNuke"  3 - Adicionar a três linhas seguintes ao ficheiro "MainView.ascx":     %@ Register Src="ViewEntry.ascx" TagName="ViewEntry" TagPrefix="uc3" %&gt; 
%@ Register Src="BlogFeed.ascx" TagName="BlogFeed" TagPrefix="uc1" %&gt; 
%@ Register Src="ViewBlog.ascx" TagName="ViewBlog" TagPrefix="uc2" %&gt;


4 - A última alteração vai corrigir os links "hard coded" para os directórios de imagens. Em todos os ficheiros da "User Interface" substituir todas a referencias "~/desktopmodules/Blog/Images/"  por "~/desktopmodules/Ankaa_Blog/Images/"

5 - Fazer "build"da solução e confirmar que apenas existem erros de formatação.  Se existirem mais erros em ficheiros *.vb, estes provavelmente desaparecerão após abertura do ficheiro *.ascx correspondente. A fase seguinte só poderá ser realizada quando não existir nenhum erro nos ficheiros *.vb 
  

  Configurar as "Module Definitions" 
    

  1 - Fazer "rename" ao ficheiro "DNN_Blog.dnn" para "DNN_Ankaa_Blog.dnn". 

  

  2 - Editar o ficheiro "DNN_Ankaa_Blog.dnn" e substituir todo o seu conteúdo do ficheiro :  

  

   Download File


1 - Lançar o portal (Ctrl + F5) e fazer login com a conta "Host"  

  2 - Aceder à opção de menu "Host" "Module Definitions"



3 - Escolher a opção "Create New Module" 
    

    

  

  4 - Escolher o "Manifest" alterado anteriormente, o "DNN_Ankaa_Blog.dnn"

  

  5 - Clicar sobre o link "Create Module from selected manifest file". O módulo fica instalado e configurado no portal:



A partir deste momento o novo blog "Ankaa_Blog" pode ser adicionado a uma página:&lt;img src="http://feeds.feedburner.com/~r/AnkaanetArtigos/~4/S_LYXCnWrZ4" height="1" width="1"/&gt;</description><dc:creator>Migasp</dc:creator><pubDate>Sun, 08 Jun 2008 22:04:20 GMT</pubDate><guid isPermaLink="false">f1397696-738c-4295-afcd-943feb885714:13</guid><feedburner:origLink>http://www.ankaa.net/Artigos/tabid/114/articleType/ArticleView/articleId/13/Default.aspx</feedburner:origLink></item></channel></rss>

