C# - Interfaces - Implementando ICloneable


Hoje voltamos a tratar com interfaces mostrando desta vez a implementação da interface ICloneable na linguagem C#.

A interface ICloneable dá suporte à clonagem, que cria uma nova instância de uma classe com o mesmo valor de uma instância existente.

Esta interface contém o método Clone que dá suporte à clonagem além do que é fornecido pelo método  Object.MemberwiseClone que cria uma cópia superficial de Object atual.

O conceito de clonagem de objetos pode ter duas implementações :

  1. Shallow (superficial): Clona o objeto mas copia apenas as referências para os objetos que referencia, exceção feita para os tipos por valor (value types) cujos valores são, por definição, sempre copiados.
  2. Deep (profunda): Clona o objeto e todos os objetos por ele referenciados.

Uma implementação do método Clone pode executar uma cópia profunda ou uma cópia superficial.

Como os chamadores do Clone não podem depender do método que executa uma operação de clonagem previsível, a MSDN recomanda que a interface ICloneable não seja implementado em APIs públicas.

A seguir veremos um exemplo básico de implementação de ICloneable.

Vamos criar um projeto Console  App (.NET Framework) usando o VS 2017 Community com o nome CShp_ICloneable;

A implementação da interface ICloneable requer apenas que a implementação do método Clone() retorne uma cópia da instância do objeto atual.

Inclua no arquivo Program o código abaixo:

using System;
namespace CShp_ICloneable
{
    public class Pessoa : ICloneable
    {
        public string Nome { get; set; }
        public int Idade { get; set; }
        public Pessoa(){}
        public Pessoa(Pessoa p)
        {
            this.Nome = p.Nome;
            this.Idade = p.Idade;
        }

         public object Clone()
        {
            return new Pessoa(this);
        }
    }
    class Program
    {
        static void Main(string[] args)
        {
            //Cria objeto do tipo Pessoa
            Pessoa macoratti = new Pessoa { Nome = "Macoratti", Idade = 55 };
            //Clone o objeto
            Pessoa macoratti_clone = (Pessoa)macoratti.Clone();
            //Valores de macoratti
            Console.WriteLine("Macoratti");
            Console.WriteLine(macoratti.Nome + " " + macoratti.Idade);
            //Valores de macoratti_clone
            Console.WriteLine("Macoratti Clone");
            Console.WriteLine(macoratti_clone.Nome + " " + macoratti_clone.Idade);
            Console.ReadLine();
        }
    }
}

Neste código implementamos a interface ICloneabe e verificamos que o objeto original e o seu clone possuem os mesmos valores de propriedades.

Veja o resultado da execução do código acima:

Vamos supor que a classe Pessoa tenha a propriedade Endereco do tipo Endereco e que as classes estão definidas da seguinte forma:

public class Pessoa
{
    public string Nome { get; set; }
    public int Idade { get; set; }
    public Endereco Endereco { get; set; }

    public Pessoa(string nome, int idade, Endereco endereco)
    {
        Nome = nome;
        Idade = idade;
        Endereco = endereco;
    }

    public string Info()
    {
        return String.Concat(this.Nome, ", ", this.Idade, " anos, Endereço: ", Endereco.Rua, " - ", CEP: ", Endereco.Cep);
    }
}

public class Endereco
{
    public string Rua { get; set; }
    public string Cep { get; set; }

    public Endereco(string rua, string cep)
    {
        Rua = rua;
        Cep = cep;
    }
}

Temos agora o objeto Endereco referenciado no objeto Pessoa. Para implementar ICloneable na classe Pessoa de forma que os endereços também sejam clonados, a classe Endereco também tem que implementar ICloneable.

Veja como fica o código:

public class Pessoa : ICloneable
{
    public string Nome { get; set; }
    public int Idade { get; set; }
    public Endereco Endereco { get; set; }

    public Pessoa(string nome, int idade, Endereco endereco)
    {
        Nome = nome;
        Idade = idade;
        Endereco = endereco;
    }

    public object Clone()
    {
        Pessoa pessoa = (Pessoa)this.MemberwiseClone();
        pessoa.Endereco = (Endereco)this.Endereco.Clone();
        return pessoa;
    }

    public string Info()
    {
        return String.Concat(this.Nome, ", ", this.Idade, " anos, Endereço: ", Endereco.Rua, ", CEP: ", Endereco.Cep);
    }
}

public class Endereco : ICloneable
{
    public string Rua { get; set; }
    public string Cep { get; set; }

    public Endereco(string rua, string cep)
    {
        Rua = rua;
        Cep = cep;
    }

      public object Clone()
      {
            return this.MemberwiseClone();
      }

}

A implementação fez uma cópia profunda da classe Pessoa copiando seus membros e a seguir criou uma nova instância de Endereco chamando o seu método Clone.

Agora para verificar a clonegam do objeto do tipo Pessoa contendo as propriedades do Endereco inclua o código abaixo no método Main() da classe Program:

   class Program
    {
        static void Main(string[] args)
        {
            Pessoa maria = new Pessoa("Maria", 39, new Endereco("Av. Paulista", "123456789"));
            Pessoa marta = (Pessoa)maria.Clone(); // Clona os valores dos atributos
     
            Console.WriteLine(maria.Info());
            Console.WriteLine(marta.Info());
            Console.WriteLine();
            Console.ReadKey();
        }
    }

A seguir o resultado da execução do código acima:

Vemos assim as propriedades do Endereco clonadas no clone do objeto Pessoa.

Em outro artigo trataremos de como realizar uma cópia profunda e quais as alternativas para fazer isso.

Pegue o código do exemplo aqui :  CShp_ICloneable.zip

"Guia-me na tua verdade, e ensina-me, pois tu és o Deus da minha salvação; por ti estou esperando todo o dia."
Salmos 25:5

Veja os Destaques e novidades do SUPER DVD Visual Basic (sempre atualizado) : clique e confira !

Quer migrar para o VB .NET ?

Quer aprender C# ??

Quer aprender os conceitos da Programação Orientada a objetos ?

Quer aprender o gerar relatórios com o ReportViewer no VS 2013 ?

Referências:


José Carlos Macoratti