首页 文章

如何在C#中克隆通用列表?

提问于
浏览
488

我在C#中有一个通用的对象列表,并希望克隆列表 . 列表中的项目是可复制的,但似乎没有选项可以执行 list.Clone() .

这有简单的方法吗?

23 回答

  • 0

    您可以使用扩展方法 .

    static class Extensions
    {
        public static IList<T> Clone<T>(this IList<T> listToClone) where T: ICloneable
        {
            return listToClone.Select(item => (T)item.Clone()).ToList();
        }
    }
    
  • 12

    如果您的元素是值类型,那么您可以这样做:

    List<YourType> newList = new List<YourType>(oldList);
    

    但是,如果它们是引用类型并且您需要深层复制(假设您的元素正确实现 ICloneable ),您可以执行以下操作:

    List<ICloneable> oldList = new List<ICloneable>();
    List<ICloneable> newList = new List<ICloneable>(oldList.Count);
    
    oldList.ForEach((item) =>
        {
            newList.Add((ICloneable)item.Clone());
        });
    

    显然,在上面的泛型中替换 ICloneable 并使用实现 ICloneable 的元素类型进行强制转换 .

    如果您的元素类型不支持 ICloneable 但确实有复制构造函数,那么您可以这样做:

    List<YourType> oldList = new List<YourType>();
    List<YourType> newList = new List<YourType>(oldList.Count);
    
    oldList.ForEach((item)=>
        {
            newList.Add(new YourType(item));
        });
    

    就个人而言,我会避免 ICloneable ,因为需要保证所有成员的深层副本 . 相反,我建议复制构造函数或像 YourType.CopyFrom(YourType itemToCopy) 这样的工厂方法返回 YourType 的新实例 .

    这些选项中的任何一个都可以通过方法(扩展名或其他方式)进行包装 .

  • 15
    public static object DeepClone(object obj) 
    {
      object objResult = null;
      using (MemoryStream  ms = new MemoryStream())
      {
        BinaryFormatter  bf =   new BinaryFormatter();
        bf.Serialize(ms, obj);
    
        ms.Position = 0;
        objResult = bf.Deserialize(ms);
      }
      return objResult;
    }
    

    这是使用C#和.NET 2.0实现此目的的一种方法 . 您的对象需要 [Serializable()] . 目标是丢失所有引用并构建新引用 .

  • 1

    对于浅表副本,您可以改为使用泛型List类的GetRange方法 .

    List<int> oldList = new List<int>( );
    // Populate oldList...
    
    List<int> newList = oldList.GetRange(0, oldList.Count);
    

    引用自:Generics Recipes

  • 2

    稍作修改后你也可以克隆:

    public static T DeepClone<T>(T obj)
    {
        T objResult;
        using (MemoryStream ms = new MemoryStream())
        {
            BinaryFormatter bf = new BinaryFormatter();
            bf.Serialize(ms, obj);
            ms.Position = 0;
            objResult = (T)bf.Deserialize(ms);
        }
        return objResult;
    }
    
  • 2

    除非您需要实际克隆 List<T> 中的每个对象,否则克隆列表的最佳方法是创建一个新列表,其中旧列表作为集合参数 .

    List<T> myList = ...;
    List<T> cloneOfMyList = new List<T>(myList);
    

    myList 的更改(例如插入或移除)不会影响 cloneOfMyList ,反之亦然 .

    但是,两个列表包含的实际对象仍然相同 .

  • 430

    要克隆列表,只需调用.ToList()

    Microsoft (R) Roslyn C# Compiler version 2.3.2.62116
    Loading context from 'CSharpInteractive.rsp'.
    Type "#help" for more information.
    > var x = new List<int>() { 3, 4 };
    > var y = x.ToList();
    > x.Add(5)
    > x
    List<int>(3) { 3, 4, 5 }
    > y
    List<int>(2) { 3, 4 }
    >
    
  • 1

    使用AutoMapper(或您喜欢的任何映射库)来克隆是简单且可维护的 .

    定义您的映射:

    Mapper.CreateMap<YourType, YourType>();
    

    做魔术:

    YourTypeList.ConvertAll(Mapper.Map<YourType, YourType>);
    
  • 3

    如果你只关心 Value 类型......

    你知道的类型:

    List<int> newList = new List<int>(oldList);
    

    如果您之前不知道类型,则需要辅助函数:

    List<T> Clone<T>(IEnumerable<T> oldList)
    {
        return newList = new List<T>(oldList);
    }
    

    正义:

    List<string> myNewList = Clone(myOldList);
    
  • 3

    如果您已在项目中引用了Newtonsoft.Json,并且您的对象可序列化,则可以始终使用:

    List<T> newList = JsonConvert.DeserializeObject<T>(JsonConvert.SerializeObject(listToCopy))
    

    可能不是最有效的方法,但除非你做了1000次,否则你可能甚至没有注意到速度差异 .

  • 15
    public static Object CloneType(Object objtype)
    {
        Object lstfinal = new Object();
    
        using (MemoryStream memStream = new MemoryStream())
        {
            BinaryFormatter binaryFormatter = new BinaryFormatter(null, new StreamingContext(StreamingContextStates.Clone));
            binaryFormatter.Serialize(memStream, objtype); memStream.Seek(0, SeekOrigin.Begin);
            lstfinal = binaryFormatter.Deserialize(memStream);
        }
    
        return lstfinal;
    }
    
  • 67
    public class CloneableList<T> : List<T>, ICloneable where T : ICloneable
    {
      public object Clone()
      {
        var clone = new List<T>();
        ForEach(item => clone.Add((T)item.Clone()));
        return clone;
      }
    }
    
  • 75
    public List<TEntity> Clone<TEntity>(List<TEntity> o1List) where TEntity : class , new()
        {
            List<TEntity> retList = new List<TEntity>();
            try
            {
                Type sourceType = typeof(TEntity);
                foreach(var o1 in o1List)
                {
                    TEntity o2 = new TEntity();
                    foreach (PropertyInfo propInfo in (sourceType.GetProperties()))
                    {
                        var val = propInfo.GetValue(o1, null);
                        propInfo.SetValue(o2, val);
                    }
                    retList.Add(o2);
                }
                return retList;
            }
            catch
            {
                return retList;
            }
        }
    
  • 3

    您也可以使用 ToArray 将列表转换为数组,然后使用 Array.Clone(...) 克隆数组 . 根据您的需要,Array类中包含的方法可以满足您的需求 .

  • 20

    您可以使用扩展方法:

    namespace extension
    {
        public class ext
        {
            public static List<double> clone(this List<double> t)
            {
                List<double> kop = new List<double>();
                int x;
                for (x = 0; x < t.Count; x++)
                {
                    kop.Add(t[x]);
                }
                return kop;
            }
       };
    
    }
    

    您可以使用其值类型成员克隆所有对象,例如,考虑以下类:

    public class matrix
    {
        public List<List<double>> mat;
        public int rows,cols;
        public matrix clone()
        { 
            // create new object
            matrix copy = new matrix();
            // firstly I can directly copy rows and cols because they are value types
            copy.rows = this.rows;  
            copy.cols = this.cols;
            // but now I can no t directly copy mat because it is not value type so
            int x;
            // I assume I have clone method for List<double>
            for(x=0;x<this.mat.count;x++)
            {
                copy.mat.Add(this.mat[x].clone());
            }
            // then mat is cloned
            return copy; // and copy of original is returned 
        }
    };
    

    注意:如果对复制(或克隆)进行任何更改,则不会影响原始对象 .

  • 2

    如果您需要具有相同容量的克隆列表,可以尝试以下操作:

    public static List<T> Clone<T>(this List<T> oldList)
    {
        var newList = new List<T>(oldList.Capacity);
        newList.AddRange(oldList);
        return newList;
    }
    
  • 2

    我的朋友Gregor Martinovic和我使用JavaScript Serializer提出了这个简单的解决方案 . 没有必要将类标记为Serializable,并且在我们的测试中使用Newtonsoft JsonSerializer比使用BinaryFormatter更快 . 扩展方法可用于每个对象 .

    标准.NET JavascriptSerializer选项:

    public static T DeepCopy<T>(this T value)
    {
        JavaScriptSerializer js = new JavaScriptSerializer();
    
        string json = js.Serialize(value);
    
        return js.Deserialize<T>(json);
    }
    

    使用Newtonsoft JSON加快选项:

    public static T DeepCopy<T>(this T value)
    {
        string json = JsonConvert.SerializeObject(value);
    
        return JsonConvert.DeserializeObject<T>(json);
    }
    
  • 0

    我为自己做了一些扩展,它转换ICollection没有实现IClonable的项目

    static class CollectionExtensions
    {
        public static ICollection<T> Clone<T>(this ICollection<T> listToClone)
        {
            var array = new T[listToClone.Count];
            listToClone.CopyTo(array,0);
            return array.ToList();
        }
    }
    
  • 0

    我使用automapper来复制一个对象 . 我只是设置了一个映射,将一个对象映射到自身 . 您可以以任何方式包装此操作 .

    http://automapper.codeplex.com/

  • 0

    以下代码应转移到列表中,只需进行少量更改 .

    基本上它通过在每个连续循环中从更大范围插入新的随机数来工作 . 如果已存在与其相同或更高的数字,则将这些随机数向上移一,以便将它们转移到新的更大范围的随机索引中 .

    // Example Usage
    int[] indexes = getRandomUniqueIndexArray(selectFrom.Length, toSet.Length);
    
    for(int i = 0; i < toSet.Length; i++)
        toSet[i] = selectFrom[indexes[i]];
    
    
    private int[] getRandomUniqueIndexArray(int length, int count)
    {
        if(count > length || count < 1 || length < 1)
            return new int[0];
    
        int[] toReturn = new int[count];
        if(count == length)
        {
            for(int i = 0; i < toReturn.Length; i++) toReturn[i] = i;
            return toReturn;
        }
    
        Random r = new Random();
        int startPos = count - 1;
        for(int i = startPos; i >= 0; i--)
        {
            int index = r.Next(length - i);
            for(int j = startPos; j > i; j--)
                if(toReturn[j] >= index)
                    toReturn[j]++;
            toReturn[i] = index;
        }
    
        return toReturn;
    }
    
  • 325

    另一件事:你可以使用反射 . 如果你正确地缓存它,那么它将在5.6秒内克隆1,000,000个对象(遗憾的是,内部对象为16.4秒) .

    [ProtoContract(ImplicitFields = ImplicitFields.AllPublic)]
    public class Person
    {
           ...
          Job JobDescription
           ...
    }
    
    [ProtoContract(ImplicitFields = ImplicitFields.AllPublic)]
    public class Job
    {...
    }
    
    private static readonly Type stringType = typeof (string);
    
    public static class CopyFactory
    {
        static readonly Dictionary<Type, PropertyInfo[]> ProperyList = new Dictionary<Type, PropertyInfo[]>();
    
        private static readonly MethodInfo CreateCopyReflectionMethod;
    
        static CopyFactory()
        {
            CreateCopyReflectionMethod = typeof(CopyFactory).GetMethod("CreateCopyReflection", BindingFlags.Static | BindingFlags.Public);
        }
    
        public static T CreateCopyReflection<T>(T source) where T : new()
        {
            var copyInstance = new T();
            var sourceType = typeof(T);
    
            PropertyInfo[] propList;
            if (ProperyList.ContainsKey(sourceType))
                propList = ProperyList[sourceType];
            else
            {
                propList = sourceType.GetProperties(BindingFlags.Public | BindingFlags.Instance);
                ProperyList.Add(sourceType, propList);
            }
    
            foreach (var prop in propList)
            {
                var value = prop.GetValue(source, null);
                prop.SetValue(copyInstance,
                    value != null && prop.PropertyType.IsClass && prop.PropertyType != stringType ? CreateCopyReflectionMethod.MakeGenericMethod(prop.PropertyType).Invoke(null, new object[] { value }) : value, null);
            }
    
            return copyInstance;
        }
    

    我通过使用Watcher类以一种简单的方式测量它 .

    var person = new Person
     {
         ...
     };
    
     for (var i = 0; i < 1000000; i++)
     {
        personList.Add(person);
     }
     var watcher = new Stopwatch();
     watcher.Start();
     var copylist = personList.Select(CopyFactory.CreateCopyReflection).ToList();
     watcher.Stop();
     var elapsed = watcher.Elapsed;
    

    RESULT: 使用内部对象PersonInstance - 16.4,PersonInstance = null - 5.6

    CopyFactory只是我的测试类,我有十几个测试,包括表达式的使用 . 您可以在扩展程序中以其他形式实现此功能 . 不要忘记缓存 .

    我还没有测试序列化,但我对百万级课程的改进表示怀疑 . 我会尝试一些快速的protobuf / newton .

    P.S . :为了简单起见,我在这里只使用了自动 property . 我可以使用FieldInfo进行更新,或者您应该通过自己的方式轻松实现 .

    我最近测试了Protocol Buffers具有DeepClone功能的串行器开箱即用 . 它在一百万个简单对象上以4.2秒获胜,但是当涉及内部对象时,它以7.4秒的结果获胜 .

    Serializer.DeepClone(personList);
    

    SUMMARY: 如果您无法访问这些课程,那么这将有所帮助 . 否则它取决于对象的数量 . 我认为你可以使用多达10,000个对象的反射(可能少一点),但是对于更多,协议缓冲序列化器将表现更好 .

  • 7

    有一种使用JSON序列化器和反序列化器在C#中克隆对象的简单方法 .

    您可以创建扩展类:

    using Newtonsoft.Json;
    
    static class typeExtensions
    {
        [Extension()]
        public static T jsonCloneObject<T>(T source)
        {
        string json = JsonConvert.SerializeObject(source);
        return JsonConvert.DeserializeObject<T>(json);
        }
    }
    

    克隆和对象:

    obj clonedObj = originalObj.jsonCloneObject;
    
  • 13
    //try this
     List<string> ListCopy= new List<string>(OldList);
     //or try
     List<T> ListCopy=OldList.ToList();
    

相关问题