Escrito em June 24th, 2009 as 8:00 am por Guilherme Bacellar

1 Comentário

Vamos a um código para clonar objetos sem utilizar serialização (o que cria um monte de problemas) e de quebra ainda ganha muito em performance.

Infelizmente esse é um de meus poucos posts feitos somente em C# (peço desculpa aos programadores em VB, mas, esse código foi hardcore para fazer).

1-) Classe de Serialização Tipada

using System;
using System.Collections;
using System.Collections.Generic;
using System.Reflection;

namespace AdvancedClone
{
    /// <summary>
    /// Generic Clone Class
    /// </summary>
    public sealed class TypedCloneStrategy<T>
    {
        /// <summary>
        /// Cache para os Campos do(s) Tipo(s) Utilizado(s)
        /// </summary>
        private readonly Dictionary<Type, Dictionary<string, FieldInfo>> _FieldsCache =
new Dictionary<Type, Dictionary<string, FieldInfo>>();

        /// <summary>
        /// Clona um Objeto
        /// </summary>
        /// <param name="target"></param>
        /// <returns></returns>
        public T Clone(T target)
        {
            // Create Objetos
            object resultSet;
            Dictionary<string, FieldInfo> typeFields;
            ConstructorInfo typeLessConstructor;
            Type isICloneType;
            ICloneable iClone;
            Type isIEnumerableType;
            IEnumerable iEnum;
            Type isIListType;
            Type isIDicType;
            IList list;
            IDictionary dic;

            // Retreave Default TypeLess Constructor (Public or NonPublic)
            typeLessConstructor =
target.GetType().GetConstructor(BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance,
null, new Type[] {}, null);

            // Initialize ResultSet Object
            resultSet = typeLessConstructor.Invoke(null);

            // Retreave All Type Field's
            typeFields = GetFields(target.GetType());

            // Looping on Fields
            foreach (KeyValuePair<string, FieldInfo> info in typeFields)
            {
                //Query if the fiels support the ICloneable Interface
                isICloneType = info.Value.FieldType.GetInterface("ICloneable", false);

                // Supports ICloneable Interface, Then, Use Direct Clone Approach
                if (isICloneType != null)
                {
                    // Getting the ICloneable interface from the object.
                    iClone = (ICloneable) info.Value.GetValue(target);

                    // If Value Are NotNull
                    if (iClone != null)
                    {
                        object clonedObject = iClone.Clone();

                        //We use the clone method to set the new value to the field.
                        info.Value.SetValue(resultSet, clonedObject);
                    }
                }
                else // Property Not Support ICloneable Interface
                {
                    info.Value.SetValue(resultSet, info.Value.GetValue(target));
                }

                // Check If Object Supports a IEnumerable interface so if it does we need to enumerate all its items and check if they support the ICloneable interface.
                isIEnumerableType = info.Value.FieldType.GetInterface("IEnumerable", false);

                // Check Result for IEnumerable Interface Search
                if (isIEnumerableType != null)
                {
                    // Get the IEnumerable interface from the field.
                    iEnum = (IEnumerable) info.Value.GetValue(target);

                    // Get The IList and the IDictionary Interfaces to iterate on collections.
                    isIListType = info.Value.FieldType.GetInterface("IList", false);
                    isIDicType = info.Value.FieldType.GetInterface("IDictionary", false);

                    // If Collection Supports a IList Interface
                    if (isIListType != null)
                    {
                        // Getting the IList interface.
                        list = (IList) info.Value.GetValue(resultSet);

                        // Looping on IEnumerable Itens
                        if (list != null)
                        {
                            foreach (object obj in iEnum)
                            {
                                // Checking to see if the current item support the ICloneable interface.
                                isICloneType = obj.GetType().GetInterface( "ICloneable", false);

                                // Supports a Clone Type
                                if (isICloneType != null)
                                {
                                    // If it does support the ICloneable interface,
                                    // we use it to set the clone of
                                    // the object in the list.
                                    ICloneable clone = (ICloneable) obj;

                                    list.Add(clone.Clone());
                                }
                            }
                        }
                    }
                    else if (isIDicType != null) // IDictionary Interface
                    {
                        // Getting the dictionary interface.
                        dic = (IDictionary) info.Value.GetValue(resultSet);

                        // Looping on
                        if (iEnum != null)
                        {
                            foreach (DictionaryEntry de in iEnum)
                            {
                                // Checking to see if the item support the ICloneable interface.
                                isICloneType = de.Value.GetType().GetInterface( "ICloneable", false);

                                if (isICloneType != null)
                                {
                                    ICloneable clone = (ICloneable) de.Value;
                                    dic[de.Key] = clone.Clone();
                                }
                            }
                        }
                    }
                 }
             }

             // Returns a Clonned ResultSet Object
            return (T) resultSet;
        }

        /// <summary>
        /// Recupera os Fields de um Objeto pelo Tipo
        /// </summary>
        /// <param name="type"></param>
        /// <returns></returns>
        private Dictionary<string, FieldInfo> GetFields(Type type)
        {
            // Verifica no Cache
            if (!_FieldsCache.ContainsKey(type))
            {
                lock (typeof (TypedCloneStrategy<T>))
                {
                    if (!_FieldsCache.ContainsKey(type))
                    {
                        _FieldsCache.Add(type, GetAllFields(type));
                    }
                }
            }

            // Retorna do Cache
            return _FieldsCache[type];
        }

        /// <summary>
        /// Método para Extrair os Fields de um Determinado Objeto
        /// </summary>
        /// <param name="type"></param>
        /// <returns></returns>
        private Dictionary<string, FieldInfo> GetAllFields(Type type)
        {
            // Cria Objeto
            Dictionary<string, FieldInfo> fields = new Dictionary<string, FieldInfo>();

            foreach (FieldInfo info in type.GetFields(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.FlattenHierarchy))
            {
                if (!fields.ContainsKey(info.Name))
                {
                    fields.Add(info.Name, info);
                }
            }

            if (type.BaseType != null)
            {
                Dictionary<string, FieldInfo> baseFields = GetAllFields(type.BaseType);

                foreach (KeyValuePair<string, FieldInfo> pair in baseFields)
                {
                    if (!fields.ContainsKey(pair.Key))
                    {
                        fields.Add(pair.Key, pair.Value);
                    }
                }
            }

            return fields;
        }
    }
}

2-) Usando a Classe

Para usar a classe, precisamos de um objeto de origem e uma variável de destino.

MeuObjetoDestino x = new AdvancedClone.TypedCloneStrategy<TipoDoMeuObjeto>().Clone(meuObjetoOrigem);

Posts Relacionados:

  1. Calculando CRC de Strings (Texto), Array’s e Arquivos
  2. Manipulando Arquivos e Diretórios no .NET
  3. Convertendo Caminhos Absolutos e URL’s Absolutas para Caminhos Relativos e URL’s Relativas
  4. Convertendo String Delimitada por Tamanho (Trancode) em Array
  5. Compressão de (Compactar) Dados
  6. Algoritmo para Validação de CPF
  7. Ordenação de Listas (Coleções)
, ,

1 Reposta to “Clonando Objetos (.Clone()) sem Serialização”


  1. Carlos

    1 year ago

    Muito bom o artigo hein cara…
    Parabéns…! :-)

Deixa uma Resposta

znjdb32s6g