How do I clone a Collection?

2019-06-22 14:42发布

问题:

I have an enumeration, Fruit, and a class, FruitCollection, which derives Collection<Fruit>. I couldn't find a way to clone FruitCollection using .NET and I found this MSDN article which defined a DeepClone() function and used MemberwiseClone(). Now, since this is an enumeration, I don't think I need to "deep" clone it, so I thought MemberwiseClone() would be sufficient. However, when I try it in PowerShell, the cloned object seems to simply be a pointer to the original object and not a clone. What am I doing wrong?

Is there another way to simply clone a Collection? FruitCollection has no other custom members.

C# code:

public enum Fruit
{
    Apple = 1,
    Orange = 2
}

public class FruitCollection : Collection<Fruit>
{
    public FruitCollection Clone()
    {
        return Clone(this);
    }

    public static FruitCollection Clone(FruitCollection fruitCollection)
    {
        return (FruitCollection)fruitCollection.MemberwiseClone();
    }

}

PowerShell Output:

PS> $basket1 = New-Object TestLibrary.FruitCollection
PS> $basket1.Add([TestLibrary.Fruit]::Apple)
PS> $basket2 = $basket1.Clone()
PS> $basket1.Add([TestLibrary.Fruit]::Orange)
PS> $basket2
Apple
Orange

回答1:

As others have pointed out in the comments, you can use the constructor that already exists on Collection and then in your Clone, create a new list for the new Collection to use so adding to basket1 doesn't affect basket2 and so forth.

public class FruitCollection : Collection<Fruit>
{
    public FruitCollection(IList<Fruit> source) : base(source)
    {
    }

    public FruitCollection()
    {
    }

    public FruitCollection Clone()
    {
        return Clone(this);
    }

    public static FruitCollection Clone(FruitCollection fruitCollection)
    {
        // ToList() will give a new List. Otherwise Collection will use the same IList we passed.
        return new FruitCollection(fruitCollection.ToList());
    }

}

void Main()
{
    var basket1 = new FruitCollection();
    basket1.Add(Fruit.Apple);
    var basket2 = basket1.Clone();
    basket2.Add(Fruit.Orange);
    Console.WriteLine("{0}", basket1.Count);
    Console.WriteLine("{0}", basket2.Count);
}


回答2:

It's because you are doing a Memberwise clone, which is a shallow copy of the non-static fields of the object. Since your object is a Collection (reference type), it is only copying a reference to the collection.

Try this instead:

public class FruitCollection : Collection<Fruit>
{
    public FruitCollection Clone()
    {
        return Clone(this);
    }

    public static FruitCollection Clone(FruitCollection fruitCollection)
    {
        var clonedFruitCollection = new FruitCollection();

        // Deep copy the collection instead of copying the reference with MemberwiseClone()
        foreach (var fruit in fruitCollection)
        {
            clonedFruitCollection.Add(fruit);
        }

        return clonedFruitCollection;
    }
}


回答3:

Create a new FruitCollection and add all elements to it. You can also create a constructor which does this, see this http://msdn.microsoft.com/en-us/library/fkbw11z0%28v=vs.110%29.aspx.