Is The Sky Really The Limit? (Site pessoal de Miguel Prego Gaspar)
 
 
Syndication

Categorias
Do site ...

Locations of visitors to this page





Artigos

Artigos actuais

Herança
sexta-feira, 20 de Junho de 2008 :: 355 Visualizações :: 0 Comentários :: :: Básicos

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 & " " & 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 & ", " & 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 & " " & LastName 
End Function

  

'Na classe Derivada 
Public Overrides Function CompleteName() As String 
    Dim result As String = MyBase.CompleteName 
    If Title <> "" Then result = Title & " " & 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

 

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:

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 >= 18) 
    End Get 
End Property

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:

    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:
    1. Um construtor, que aceite como argumento uma string, que será o nome da categoria a ser utilizada.
    2. Um propriedade chamada "Categoria", que será afectada pelo valor recebido pelo construtor. O "Set" da categoria deverá implementar a seguinte lógica:
      1. Se a categoria for "mensagens", "avisos" ou "erros" atribuir o valor recebido.
      2. Se for outro valor, atribuir o valor "geral"
    3. Um método "Overridable" chamado "GetDate" que devolve a data actual.
    4. 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:
    1. Um construtor que aceita a categoria.
    2. 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

      

      

powered by metaPost

Rating

Comentários
Ainda não existem comentários. Seja o primeiro a colocar um!
Clique aqui para colocar um comentário.
 
 
   
ankaa.net@2009