【问题标题】:How do I clone a Collection<T>?如何克隆 Collection<T>?
【发布时间】:2014-11-25 10:12:54
【问题描述】:

我有一个枚举 Fruit 和一个类 FruitCollection,它派生出 Collection&lt;Fruit&gt;。我找不到使用.NET 克隆FruitCollection 的方法,我发现this MSDN article 定义了DeepClone() 函数并使用了MemberwiseClone()。现在,由于这是一个枚举,我认为我不需要“深度”克隆它,所以我认为MemberwiseClone() 就足够了。但是,当我在 PowerShell 中尝试时,克隆的对象似乎只是指向原始对象的指针,而不是克隆。我做错了什么?

还有其他方法可以简单地克隆 Collection 吗? FruitCollection 没有其他自定义成员。

C#代码:

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 输出:

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

【问题讨论】:

  • @TimSchmelter:你是说Fruit 是引用类型吗?
  • @TimSchmelter 这是object.MemberwiseClone()(即protected)。
  • object.MemberwiseClone() 不会克隆集合的内容。它只是复制对象的字段——在这种情况下,其中一个字段将是一个数组(或其他引用集合类型),并且只复制引用。
  • 谢谢,@MatthewWatson。那么,如何克隆FruitCollection内容

标签: c# powershell collections clone


【解决方案1】:

正如其他人在 cmets 中指出的那样,您可以使用 Collection 上已经存在的构造函数,然后在 Clone 中,为新的 Collection 创建一个新列表以供使用,以便添加到 basket1 不会影响 basket2 和以此类推。

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);
}

【讨论】:

  • 谢谢,@TyCobb。我没有意识到FruitCollection 不会自动派生Collection&lt;T&gt;(IList&lt;T&gt; list) 构造函数。
  • @Hossy,是的,你必须在继承时手动定义你想要的构造函数。
  • 嗯.. 我总是为此而堕落。我喜欢自动继承。
【解决方案2】:

这是因为您正在执行 Memberwise 克隆,它是对象非静态字段的浅表副本。由于你的对象是一个集合(引用类型),它只是复制一个对集合的引用。

试试这个:

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】:

创建一个新的 FruitCollection 并向其中添加所有元素。您还可以创建一个构造函数来执行此操作,请参阅http://msdn.microsoft.com/en-us/library/fkbw11z0%28v=vs.110%29.aspx

【讨论】:

  • 我尝试了return new Collection&lt;Fruit&gt;(fruitCollection);,但出现错误:无法将类型“System.Collections.ObjectModel.Collection”隐式转换为“TestLibrary.FruitCollection”。存在显式转换(您是否缺少演员表?)。 return (FruitCollection)new Collection&lt;Fruit&gt;(fruitCollection); 也会失败(在运行时)并出现同样的错误。
  • 当然:所有 FruitCollection 都是 Collection,但反之则不然。您需要创建一个新的 FruitCollection。并在这个类中定义你自己的拷贝构造函数。
猜你喜欢
  • 2010-10-05
  • 1970-01-01
  • 1970-01-01
  • 2017-05-19
  • 2017-01-09
  • 2011-04-25
  • 2010-10-17
相关资源
最近更新 更多