Desenvolvimento - C#

.Net Framework Inside : Reflection e Construtores

Este artigo mostra como trabalhar os Contrutores dos Objetos com Reflection.

por Guilherme Bacellar Moralez



Veja o primeiro artigo da série, clicando aqui.

Iremos começar a utilizar reflexão, para tanto, vamos adotar uma classe modelo para que possamos ter controle sobre a utilização:

C#

namespace ReflectionCSharp

{

  /// <summary>

  /// Classe de Usuário de Exemplo

  /// </summary>

  [Serializable]

  [XmlRoot("Usuário")]

  public class Usuario : System.Object, IComparable<Usuario>, INotifyPropertyChanged

  {

#region Construtores

      public Usuario() { }

public Usuario(string nome, int idade, DateTime dataNascimento)

      {

            _Nome = nome;

            _Idade = idade;

            _DataNascimento = dataNascimento;

}

      #endregion

#region Fields (Campos)

      private string _Nome;

      private int _Idade;

private DateTime _DataNascimento;

      #endregion

      #region Propriedades

      [XmlAttribute("Nome")]

      public string Nome

      {

            get { return _Nome; }

      set { _Nome = value; }

}

      public int Idade

      {

            get { return _Idade; }

            set { _Idade = value; }

}

      [XmlElement("DataNascimento")]

      public DateTime DataNascimento

      {

            get { return _DataNascimento; }

           set { _DataNascimento = value; }

}

      #endregion

      #region Eventos

      public event PropertyChangedEventHandler PropertyChanged;

#endregion

      #region Implementações dos Métodos das Interfaces Declaradas

/// <summary>

      /// Método da Interface IComparable

      /// </summary>

      /// <param name="other"></param>

      /// <returns></returns>

      public int CompareTo(Usuario other)

      {

            // Realiza Comparação de Nome

            return this.Nome.CompareTo(other.Nome);

}

      #endregion

      #region Métodos

      /// <summary>

      /// Calcula a Idade do Usuário pela Data do Nascimento

      /// </summary>

      /// <returns></returns>

      public int CalculaIdade()

      {

            // Chama o Método Privado Estático desta Classe

            return CalculaIdade(this.DataNascimento);

}

      /// <summary>

      /// Calcula a Idade do Usuário pela Data do Nascimento passada por Parâmetro

      /// </summary>

      /// <returns></returns>

      private static int CalculaIdade(DateTime dataNascimento)

      {

            TimeSpan result = DateTime.Now.Subtract(dataNascimento);

            return (int)(result.TotalDays / 365);

}

      #endregion

  }

}


VB.Net

Namespace ReflectionVBNet

    """ <summary>

    """ Classe de Usuário de Exemplo

    """ </summary>

    """ <remarks></remarks>

    <Serializable()> _

    <XmlRoot("Usuário")> _

    Public Class Usuario

        Implements INotifyPropertyChanged, IComparable(Of Usuario)

#Region "Construtores"

        Public Sub New()

        End Sub

        Public Sub New(ByVal nome As String, ByVal idade As Int32, ByVal dataNascimento As DateTime)

            _Nome = nome

            _Idade = idade

            _DataNascimento = dataNascimento

        End Sub

#End Region

#Region "Fields (Campos)"

        Private _Nome As String

        Private _Idade As Integer

        Private _DataNascimento As DateTime

#End Region

#Region "Propriedades"

        <XmlAttribute("Nome")> _

        Public Property Nome() As String

            Get

                Return _Nome

            End Get

            Set(ByVal value As String)

                _Nome = value

            End Set

        End Property

        Public Property Idade() As Integer

            Get

                Return _Idade

            End Get

            Set(ByVal value As Integer)

                _Idade = value

            End Set

        End Property

        <XmlElement("DataNascimento")> _

        Public Property DataNascimento() As Date

            Get

                Return _DataNascimento

            End Get

            Set(ByVal value As Date)

                _DataNascimento = value

            End Set

        End Property

#End Region

#Region "Eventos"

        Public Event PropertyChanged As PropertyChangedEventHandler Implements INotifyPropertyChanged.PropertyChanged

#End Region

#Region "Implementações dos Métodos das Interfaces Declaradas"

        """ <summary>

        """ Método da Interface IComparable

        """ </summary>

        """ <param name="other"></param>

        Public Function CompareTo(ByVal other As Usuario) As Integer Implements IComparable(Of Usuario).CompareTo

            Return Me.Nome.CompareTo(other.Nome)

        End Function

#End Region

#Region "Métodos"

        """ <summary>

        """ Calcula a Idade do Usuário pela Data do Nascimento

        """ </summary>

        """ <returns></returns>

        """ <remarks></remarks>

        Public Function CalculaIdade() As Integer

            "" Chama o Método Privado Estático desta Classe

            Return CalculaIdade(Me.DataNascimento)

        End Function

        """ <summary>

        """ Calcula a Idade do Usuário pela Data do Nascimento passada por Parâmetro

        """ </summary>

        """ <remarks></remarks>

        Private Function CalculaIdade(ByVal dataNascimento As DateTime) As Integer

            Dim result As TimeSpan = DateTime.Now.Subtract(dataNascimento)

            Return CType((result.TotalDays / 365), Int32)

        End Function

#End Region

    End Class

End Namespace

Essa classe foi criada para que possamos trabalhar de forma controlada com Reflection e também foi desenhada para conter todos os itens que podemos precisar para nosso estudo sobre Reflection.

Recuperando todos os Construtores

A recuperação de construtores com Reflection permite que possamos criar as instâncias de nossos objetos utilizando os recursos já implementados nos construtores.

Para tanto, utilizaremos o método (GetConstructors) do tipo desejado.

C#

// Cria Objetos

Type meuTipo;

ConstructorInfo[] constructors;

// Recupera o Tipo Desejado

meuTipo = typeof(Usuario);

// Recupera os Construtores

constructors = meuTipo.GetConstructors();


VB.Net

" Cria Objetos

Dim meuTipo As Type

Dim constructors As ConstructorInfo()

" Recupera o Tipo Desejado

meuTipo = GetType(Usuario)

" Recupera os Construtores

constructors = meuTipo.GetConstructors()

Desta forma temos uma coleção dos construtores que definem nosso objeto. Neste caso, nossa classe (Usuario) possui 2 construtores.

Recuperando um (01) Construtor Específico

Na maioria das situações precisaremos recuperar apenas 1 construtor específico e não todos eles.

Neste caso, utilizamos o método (GetConstructor) do tipo desejado.

C#

// Cria Objetos

Type meuTipo;

ConstructorInfo parameterLessConstructor;

ConstructorInfo parameterizedConstructor;

// Recupera o Tipo Desejado

meuTipo = typeof(Usuario);

// Recupera o Construtor sem Parâmetros

parameterLessConstructor = meuTipo.GetConstructor(new Type[]{});

// Recupera o Construtor com o Nome (string), Idade (int) e Data do Nascimento (DateTime)

parameterizedConstructor = meuTipo.GetConstructor(new Type[] {typeof (string), typeof (int), typeof (DateTime)});


VB.Net

" Cria Objetos

Dim meuTipo As Type

Dim parameterLessConstructor As ConstructorInfo

Dim parameterizedConstructor As ConstructorInfo

" Recupera o Tipo Desejado

meuTipo = GetType(Usuario)

" Recupera o Construtor sem Parâmetros

parameterLessConstructor = meuTipo.GetConstructor(New Type() {})

" Recupera o Construtor com o Nome (String), Idade (Integer) e Data do Nascimento (DateTime)

parameterizedConstructor = meuTipo.GetConstructor(New Type() {GetType(String), GetType(Integer), GetType(DateTime)})

Perceba que, o método (GetConstructor) “como quase todos os métodos que recuperam informações por Reflection” responde a assinatura do elemento desejado.

Então, em nosso caso, passando-se uma lista de “Tipos” vazia temos como resposta um Construtor sem parâmetros. Mas, se passarmos uma lista de “Tipos” os “Tipos” dos parâmetros obteremos de retorno o elemento que atende à assinatura dos “Tipos” especificados.

Esse conceito é muito importante na Reflexão, então, vamos guardá-lo.

E se os Construtores não forem Públicos?

Até agora está tudo fácil, mas, e se o construtor não for público?

Se por acaso, o construtor de nosso objeto (Usuario) não fosse público, o método acima retornaria nulo/nothing como resultado da recuperação do construtor.

Contudo, o mecanismo de Reflexão do .NET é extremamente poderoso e permite que recuperaremos membros “não públicos” dos objetos.

C#

// Cria Objetos

Type meuTipo;

ConstructorInfo parameterLessConstructor;

// Recupera o Tipo Desejado

meuTipo = typeof(Usuario);

// Recupera o Construtor sem Parâmetros

parameterLessConstructor = meuTipo.GetConstructor(BindingFlags.NonPublic | BindingFlags.Instance, null, new Type[]{}, null);

VB.Net

" Cria Objetos

Dim meuTipo As Type

Dim parameterLessConstructor As ConstructorInfo

" Recupera o Tipo Desejado

meuTipo = GetType(Usuario)

" Recupera o Construtor sem Parâmetros

parameterLessConstructor = meuTipo.GetConstructor(BindingFlags.NonPublic Or BindingFlags.Instance, Nothing, New Type() {}, Nothing)

Observemos o método (GetConstructor) com mais parâmetros. Dentre eles existem as BindingFlags, que permitem a mudança do escopo da Reflexão. Em nosso exemplo estamos pedindo por membros “não públicos” e “de instância”.

Observe os outros parâmetros nulos. Eles são para especificar-mos um “Binder” customizado, e modificadores de parâmetros. Não vamos entrar neles neste momento, mas, em breve vamos explicar-los em detalhes.

Sendo assim, obtemos um construtor sem parâmetros (observem o “Tipo” vazio) e que ao mesmo tempo é privado.

Este também é outro conceito muito importante do mecanismo de Reflection.

Construindo Classes

Bom, tudo isso para que possamos construir uma classe, então, vamos lá.

Nosso objeto “ConstructorInfo” contém um método chamado “Invoke” que ativa seu funcionamento, então....

C#

// Cria Objetos

Usuario novoUsuarioSemParametros;

Usuario novoUsuarioComParametros;

// Cria Novo Usuário com o Construtor sem os Parâmetros

novoUsuarioSemParametros = (Usuario)parameterLessConstructor.Invoke(null);

// Cria um Novo Usuário com o Contrutor com os 3 Parâmetros

novoUsuarioComParametros = (Usuario)parameterizedConstructor.Invoke(new object[] { "Guilherme Bacellar Moralez", 25, new DateTime(1982, 10, 12) });

VB.Net

" Cria Objetos

Dim novoUsuarioSemParametros As Usuario

Dim novoUsuarioComParametros As Usuario

" Cria Novo Usuário com o Construtor sem os Parâmetros

novoUsuarioSemParametros = CType(parameterLessConstructor.Invoke(Nothing), Usuario)

" Cria um Novo Usuário com o Contrutor com os 3 Parâmetros

novoUsuarioComParametros = CType(parameterizedConstructor.Invoke(New Object() {"Guilherme Bacellar Moralez", 25, New DateTime(1982, 10, 12)}), Usuario)

Conseguimos criar novos objetos (Usuario) com o construtor sem parâmetros e com o construtor com parâmetros.

Para finalizar, observe que o método (Invoke) recebe uma coleção de (Objetos) com os parâmetros desejados para a ativação do construtor.

Bom, de Construtores com Reflexão é só.

Até o próximo artigo sobre Reflexão.

Guilherme Bacellar Moralez

Guilherme Bacellar Moralez - Bacharel em Ciências da Computação, Desenvolvedor .NET há 4 anos, MCAD, Autor de Livros e Artigos, Arquiteto de Software do Projeto D.NET (Framework de Desenvolvimento .NET).
Blog:
http://dotnetmax.bacellar.org/