克隆列表<t> </t>

时间:2009-02-06 07:41:02

标签: c# .net

我认为要克隆一个List,你只需要调用:

List<int> cloneList = new List<int>(originalList);

但是我在我的代码中尝试了这一点,而且我似乎得到的影响意味着上面只是在做:

cloneList = originalList ...因为对cloneList的更改似乎正在影响originalList。

那么克隆List的方法是什么?

编辑:

我正在考虑做这样的事情:

public static List<T> Clone<T>(this List<T> originalList) where T : ICloneable
{
    return originalList.ConvertAll(x => (T) x.Clone());
}

EDIT2:

我拿了Binoj Antony建议的深拷贝代码并创建了这个扩展方法:

public static T DeepCopy<T>(this T original) where T : class
{
    using (MemoryStream memoryStream = new MemoryStream())
    {
        BinaryFormatter binaryFormatter = new BinaryFormatter();
        binaryFormatter.Serialize(memoryStream, original);
        memoryStream.Seek(0, SeekOrigin.Begin);
        return (T)binaryFormatter.Deserialize(memoryStream);
    }
}

EDIT3:

现在,假设列表中的项目是结构。如果我打电话会怎么样?:

List<StructType> cloneList = new List<StructType>(originalList);

我非常肯定,我会得到一个填充了新的独特项目的列表,对吗?

8 个答案:

答案 0 :(得分:21)

这样可行......

List<Foo> cloneList = new List<Foo>(originalList);

当你说“因为对cloneList的更改似乎影响了原始列表”。 - 您的意思是更改列表,还是更改项目 ...

添加/删除/交换项目正在更改列表 - 因此,如果我们这样做:

cloneList.Add(anotherItem);

您应该会发现cloneList超过originalList。但是,如果内容是引用类型(类),那么两个列表仍然指向相同的底层对象 - 所以如果我们这样做:

cloneList[0].SomeObjectProperty = 12345;

然后这也将显示针对originalList[0].SomeObjectProperty - 只有一个对象(在两个列表之间共享)。

如果这是问题,你将不得不克隆列表中的对象 - 然后你进入整个深层次与浅层问题......这是问题吗?

对于浅层副本,您可以使用与答案here非常相似的内容 - 只需使用TTo = TFrom(可能简化为单个T)。

答案 1 :(得分:16)

您可以使用以下代码制作列表的深层副本或支持序列化的任何其他对象:

此外,您可以将此用于v 2.0及更高版本的任何.NET框架版本,并且可以应用类似技术(删除泛型的使用)并在1.1中使用

public static class GenericCopier<T>
{
    public static T DeepCopy(object objectToCopy)
    {
        using (MemoryStream memoryStream = new MemoryStream())
        {
            BinaryFormatter binaryFormatter = new BinaryFormatter();
            binaryFormatter.Serialize(memoryStream, objectToCopy);
            memoryStream.Seek(0, SeekOrigin.Begin);
            return (T) binaryFormatter.Deserialize(memoryStream);
        }
    }
}

您可以使用

调用它
List<int> deepCopiedList = GenericCopier<List<int>>.DeepCopy(originalList);

测试是否有效的完整代码:

static void Main(string[] args)
{
    List<int> originalList = new List<int>(5);
    Random random = new Random();
    for(int i = 0; i < 5; i++)
    {
        originalList.Add(random.Next(1, 100));
        Console.WriteLine("List[{0}] = {1}", i, originalList[i]);
    }
    List<int> deepCopiedList = GenericCopier<List<int>>.DeepCopy(originalList);
    for (int i = 0; i < 5; i++)
        Console.WriteLine("deepCopiedList[{0}] value is {1}", i, deepCopiedList[i]);
}

答案 2 :(得分:8)

我怀疑您的实际示例会出现问题,因为int是值类型。例如:

using System;
using System.Collections.Generic;

class Test
{
    static void Main()
    {
        List<int> originalList = new List<int> { 5, 6, 7 };
        List<int> cloneList = new List<int>(originalList);

        cloneList.Add(8);
        cloneList[0] = 2;
        Console.WriteLine(originalList.Count); // Still 3
        Console.WriteLine(originalList[0]); // Still 5
    }
}

然而,正如Marc所说,如果您的列表包含可变引用类型,则克隆列表将只采用副本 - 如果您改变列表引用的对象,则这些更改将是可见的通过两个列表。替换一个列表中的元素不会改变另一个列表中的等效元素:

using System;
using System.Collections.Generic;

class Dummy
{
    public int Value { get; set; }

    public Dummy (int value)
    {
        this.Value = value;
    }
}

class Test
{
    static void Main()
    {
        List<Dummy> originalList = new List<Dummy> 
        {
            new Dummy(5),
            new Dummy(6),
            new Dummy(7)
        };

        List<Dummy> cloneList = new List<Dummy>(originalList);

        cloneList[0].Value = 1;
        cloneList[1] = new Dummy(2);
        Console.WriteLine(originalList[0].Value); // Changed to 1
        Console.WriteLine(originalList[1].Value); // Still 6
    }
}

要对元素类型实现ICloneable的列表进行“深度克隆”,请使用:

List<Foo> cloneList = originalList.ConvertAll(x => (Foo) x.Clone());

然而,这个克隆的真正深度将取决于元素类型中ICloneable的实现 - ICloneable通常被认为是一件坏事,因为它的合同是如此模糊。

答案 3 :(得分:2)

如果列表的基础类型是值类型,则使用带有原始列表作为参数的List构造函数将起作用。对于引用类型List元素,我认为您希望深度复制它们。

你可以这样做:

(假设底层类型实现了ICloneable)

originalList.ForEach((item) =>
                       {
                         cloneList.Add((ICloneable)item.Clone());
                       }
                     );

或使用一些LINQ

var cloneList = originalList.Select(item => (ICloneable)item.Clone()).ToList();

答案 4 :(得分:2)

我投票不依赖于对象序列化。这是昂贵而不好的做法。

public static TObj CloneObject<TObj>(this TObj obj)
    where TObj : ICloneable
{
    return (TObj)obj.Clone();
}

上面的方法更优雅,如果需要,你应该真正关心实现一个clonable接口。你也可以把它变成通用的。

public interface ICloneable<T> : IClonable
{
    T CloneObject();
}

或者,您可以避免使用IClonable接口作为基本类型,因为它维护得很差。方法名称必须更改为,因为您不能对返回类型执行重载。

public static List<T> CloneList(this List<T> source)
    where TObj : ICloneable
{
    return source.Select(x=>x.CloneObject()).ToList();
}

就这么简单。

虽然使用值类型可能可以解决您的问题。它们总是按副本复制。因此,只要您的数据结构符合价值,就不必克隆任何内容。

答案 5 :(得分:1)

具体说明here项目会被复制到新列表中。是的,这应该有效。使用值类型,您将获得完全独立性。但请记住,使用引用类型,列表将是独立的,但它们将指向相同的对象。您需要深度复制列表。

答案 6 :(得分:0)


            List list = new List ();
            List clone = new List (list);
            list.Add (new int ());
            Debug.Assert (list != clone);
            Debug.Assert (list.Count == 1);
            Debug.Assert (clone.Count == 0);

此代码完全符合我的预期。你是否可以更改列表中的对象?列表项不会new List(oldList)克隆。

答案 7 :(得分:0)

我必须补充一点:如果您要使用序列化来促进深度复制,为什么要克隆每个单独的项目?只需克隆整个原始列表即可。

除非您具有仅克隆满足特定条件的节点的逻辑,否则逐节点地执行此操作。