我在C#中有一个通用对象列表,并希望克隆该列表。列表中的项是可克隆的,但似乎没有list.Clone()
选项。
这个问题有简单的解决方法吗?
我在C#中有一个通用对象列表,并希望克隆该列表。列表中的项是可克隆的,但似乎没有list.Clone()
选项。
这个问题有简单的解决方法吗?
如果您的元素是值类型,那么您可以直接进行如下操作:
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
实例。
这些选项中的任何一个都可以被封装在一个方法中(扩展或其他方式)。
ICloneable
时,定义从未说明克隆是深度还是浅层的,因此您无法确定对象实现它时将执行哪种类型的克隆操作。这意味着,如果您想要对 List<T>
进行深度克隆,则必须在没有 ICloneable
的情况下进行,以确保它是深度复制。 - Jeff YatesnewList.AddRange(oldList.Select(i => i.Clone())
或 newList.AddRange(oldList.Select(i => new YourType(i)
) - phoog您可以使用扩展方法。
static class Extensions
{
public static IList<T> Clone<T>(this IList<T> listToClone) where T: ICloneable
{
return listToClone.Select(item => (T)item.Clone()).ToList();
}
}
如果需要进行一次浅拷贝,您可以使用泛型List类的GetRange方法。
List<int> oldList = new List<int>( );
// Populate oldList...
List<int> newList = oldList.GetRange(0, oldList.Count);
摘自:泛型食谱
List<int> newList = oldList.ToList()
,与此相同的效果。但是,在我看来,Arkiliknam的解决方案最易读懂。 - Dan BechardToList
,因为它避免了所有的冗余 - 我想知道哪个实际上更高效...查了一下。看起来 ToList
调用了 new List<T>
,最终会使用 Array.CopyTo
,所以大致相同。 - NetMagepublic static object DeepClone(object obj)
{
object objResult = null;
using (var ms = new MemoryStream())
{
var bf = new BinaryFormatter();
bf.Serialize(ms, obj);
ms.Position = 0;
objResult = bf.Deserialize(ms);
}
return objResult;
}
以下是使用C#和.NET 2.0的一种方法。您的对象需要[Serializable()]
属性。目标是消除所有引用并构建新引用。
要克隆一个列表,只需调用 .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 }
>
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;
}
if (!obj.GetType().IsSerializable) return default(T);
添加为第一条语句,这样就可以避免异常了。如果你把它改成扩展方法,甚至可以使用 Elvis 操作符,像这样 **var b = a?.DeepClone();
**(例如给定 var a = new List<string>() { "a", "b" };
)。 - Matt除非您需要每个对象的实际克隆,否则克隆列表的最佳方法是使用旧列表作为集合参数创建新列表。
List<T> myList = ...;
List<T> cloneOfMyList = new List<T>(myList);
< p >对myList
进行的更改,例如插入或删除操作,不会影响cloneOfMyList
,反之亦然。但是,这两个列表包含的实际对象仍然是相同的。
使用 AutoMapper(或您喜欢的任何映射库)进行克隆操作非常简单且易于维护。
定义您的映射:
Mapper.CreateMap<YourType, YourType>();
施展魔法:
YourTypeList.ConvertAll(Mapper.Map<YourType, YourType>);
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);
如果你在你的项目中已经引用了Newtonsoft.Json,并且你的对象是可序列化的,你可以随时使用:
List<T> newList = JsonConvert.DeserializeObject<T>(JsonConvert.SerializeObject(listToCopy))
可能不是最有效的方法,但除非你要执行数以千计的操作,否则甚至可能注意不到速度差异。