集合中创建所有元素深拷贝的通用方法

8

我有多种不同对象类型的ObservableCollection。我想编写一个通用方法,它可以接受任何这些对象类型的集合,并返回一个新集合,其中每个元素都是给定集合中元素的深度副本。以下是一个特定类的示例:

   private static ObservableCollection<PropertyValueRow> DeepCopy(ObservableCollection<PropertyValueRow> list)
   {
        ObservableCollection<PropertyValueRow> newList = new ObservableCollection<PropertyValueRow>();
        foreach (PropertyValueRow rec in list)
        {
            newList.Add((PropertyValueRow)rec.Clone());
        }
        return newList;
   }

我该如何使这个方法适用于任何实现了ICloneable接口的类?

5
公平地提醒,不是所有的 ICloneable 实现都是深拷贝。 - Dan Bryant
3个回答

25
你可以像这样做:

你可以像这样做:

private static ObservableCollection<T> DeepCopy<T>(ObservableCollection<T> list)
    where T : ICloneable
{
   ObservableCollection<T> newList = new ObservableCollection<T>();
   foreach (T rec in list)
   {
       newList.Add((T)rec.Clone());
   }
   return newList;
}

请注意,您可以通过使用 IEnumerable<T> 使其更通用,而 LINQ 使其更容易:
private static ObservableCollection<T> DeepCopy<T>(IEnumerable<T> list)
    where T : ICloneable
{
   return new ObservableCollection<T>(list.Select(x => x.Clone()).Cast<T>());
}

1
我已经尝试过那个方法,但似乎它并不认为那是一个通用方法。我得到了一个“非泛型声明上不允许使用约束条件”的编译器错误提示。 - bwarner
1
糟糕 - 我打错字了;你只需要在方法名后面加上 <T> - Jon Skeet
1
读了所有这些关于Jon Skeet的Chuck Norris式笑话后,我发现很难相信 - Jon Skeet在他的帖子中犯了一个错别字。但是这确实是一篇优美的文章。感谢您提供整洁的第二个基于LINQ的版本。整洁。非常整洁。 - Peter Perháč
当!这段代码在 Windows Phone 7 项目中无法运行。提示 ICloneable 不可访问!有任何解决方法吗,Skeet? - bragboy
@Bragboy:你的集合里有哪些值?你可以始终使用相同方法引入自己的接口... - Jon Skeet
@JonSkeet:是的,我最终做了类似的事情。谢谢您提供这个简洁的方法,我还有其他一些地方(非 WP7)需要用到它。 - bragboy

3
private static ObservableCollection<T> DeepCopy<T>(ObservableCollection<T> list) 
    where T : ICloneable 
{ 
   ObservableCollection<T> newList = new ObservableCollection<T>(); 
   foreach (T rec in list) 
   { 
       newList.Add((T)rec.Clone()); 
   } 
   return newList; 
} 

2
这似乎是Jon Skeet帖子的“克隆”版本 :-) - Peter Perháč
9
你认为我是如何在13:26 "克隆"出正确版本的代码片段的,当他在14:15进行了更正...我没有时间机器。 - Hinek

0
我使用了一个非常相似的函数,它可以与所有可构造的ICollections(例如许多标准集合)一起使用。
    public static TContainer CloneDeep<TContainer, T>( TContainer r ) 
        where T : ICloneable
        where TContainer: ICollection<T>, new()
    {
        // could use linq here, but this is my original pedestrian code ;-)
        TContainer l = new TContainer();
        foreach(var t in r)
        {
            l.Add( (T)t.Clone() );  
        }

        return l;
    }

不幸的是,编译器无法推断类型,因此必须显式传递它们。对于超过少数几个调用,我会编写特化版本。这里是一个关于列表(List)的示例(它本身可以使用隐式推断的T进行调用)。

    public static List<T> CloneListDeep<T>( List<T> r ) where T : ICloneable
    {
        return CloneDeep<List<T>, T>( r );
    }

我广泛使用此函数来创建列表的副本,作为对话框上数据源的基础,这些对话框可以被取消。当对话框被取消时,修改后的列表将被简单地丢弃;当对话框被确认时,编辑后的列表将简单地替换原始列表。当然,这种模式的前提是具有语义正确且维护良好的 T.clone()

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接