我在C#中有一个通用的对象列表,希望克隆该列表。列表中的项目是可克隆的,但似乎没有选择的余地list.Clone()
。
有没有解决此问题的简单方法?
clone()
根据定义不是深拷贝吗?我认为在C#中,您可以轻松地使用=传递指针。
我在C#中有一个通用的对象列表,希望克隆该列表。列表中的项目是可克隆的,但似乎没有选择的余地list.Clone()
。
有没有解决此问题的简单方法?
clone()
根据定义不是深拷贝吗?我认为在C#中,您可以轻松地使用=传递指针。
Answers:
您可以使用扩展方法。
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<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
确保它是深层副本。
newList.AddRange(oldList.Select(i => i.Clone())
或newList.AddRange(oldList.Select(i => new YourType(i)
)
对于浅表副本,可以改用通用List类的GetRange方法。
List<int> oldList = new List<int>( );
// Populate oldList...
List<int> newList = oldList.GetRange(0, oldList.Count);
引用自:泛型食谱
List<int> newList = oldList.ToList()
。效果一样。但是,我认为Arkiliknam的解决方案最易于阅读。
public static object DeepClone(object obj)
{
object objResult = null;
using (MemoryStream ms = new MemoryStream())
{
BinaryFormatter 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" };
举个例子)。
除非您需要对内部的每个对象进行实际克隆,否则List<T>
克隆列表的最佳方法是使用旧列表作为收集参数来创建新列表。
List<T> myList = ...;
List<T> cloneOfMyList = new List<T>(myList);
对myList
诸如插入或删除之类的更改不会影响cloneOfMyList
,反之亦然。
但是,两个列表包含的实际对象仍然相同。
如果您已经在项目中引用了Newtonsoft.Json并且您的对象是可序列化的,则可以始终使用:
List<T> newList = JsonConvert.DeserializeObject<T>(JsonConvert.SerializeObject(listToCopy))
可能不是最有效的方法,但是除非您重复执行1000到100次,否则您甚至可能不会注意到速度差异。
public static Object CloneType(Object objtype)
{
Object lstfinal = new Object();
using (MemoryStream memStream = new MemoryStream())
{
BinaryFormatter binaryFormatter = new BinaryFormatter(null, new StreamingContext(StreamingContextStates.Clone));
binaryFormatter.Serialize(memStream, objtype); memStream.Seek(0, SeekOrigin.Begin);
lstfinal = binaryFormatter.Deserialize(memStream);
}
return lstfinal;
}
public List<TEntity> Clone<TEntity>(List<TEntity> o1List) where TEntity : class , new()
{
List<TEntity> retList = new List<TEntity>();
try
{
Type sourceType = typeof(TEntity);
foreach(var o1 in o1List)
{
TEntity o2 = new TEntity();
foreach (PropertyInfo propInfo in (sourceType.GetProperties()))
{
var val = propInfo.GetValue(o1, null);
propInfo.SetValue(o2, val);
}
retList.Add(o2);
}
return retList;
}
catch
{
return retList;
}
}
我的朋友Gregor Martinovic和我想出了一个使用JavaScript序列化器的简单解决方案。不需要将类标记为Serializable,在我们的测试中使用Newtonsoft JsonSerializer比使用BinaryFormatter更快。使用可在每个对象上使用的扩展方法。
标准.NET JavascriptSerializer选项:
public static T DeepCopy<T>(this T value)
{
JavaScriptSerializer js = new JavaScriptSerializer();
string json = js.Serialize(value);
return js.Deserialize<T>(json);
}
使用Newtonsoft JSON的更快选项:
public static T DeepCopy<T>(this T value)
{
string json = JsonConvert.SerializeObject(value);
return JsonConvert.DeserializeObject<T>(json);
}
如果有人读过我会很幸运...但是为了不在我的Clone方法中返回类型对象的列表,我创建了一个接口:
public interface IMyCloneable<T>
{
T Clone();
}
然后我指定了扩展名:
public static List<T> Clone<T>(this List<T> listToClone) where T : IMyCloneable<T>
{
return listToClone.Select(item => (T)item.Clone()).ToList();
}
这是我的A / V标记软件中接口的实现。我想让我的Clone()方法返回一个VidMark列表(而ICloneable接口希望我的方法返回一个对象列表):
public class VidMark : IMyCloneable<VidMark>
{
public long Beg { get; set; }
public long End { get; set; }
public string Desc { get; set; }
public int Rank { get; set; } = 0;
public VidMark Clone()
{
return (VidMark)this.MemberwiseClone();
}
}
最后,扩展在类中的用法:
private List<VidMark> _VidMarks;
private List<VidMark> _UndoVidMarks;
//Other methods instantiate and fill the lists
private void SetUndoVidMarks()
{
_UndoVidMarks = _VidMarks.Clone();
}
有人喜欢吗?有什么改善吗?
您可以使用扩展方法:
namespace extension
{
public class ext
{
public static List<double> clone(this List<double> t)
{
List<double> kop = new List<double>();
int x;
for (x = 0; x < t.Count; x++)
{
kop.Add(t[x]);
}
return kop;
}
};
}
您可以使用其值类型成员来克隆所有对象,例如,考虑此类:
public class matrix
{
public List<List<double>> mat;
public int rows,cols;
public matrix clone()
{
// create new object
matrix copy = new matrix();
// firstly I can directly copy rows and cols because they are value types
copy.rows = this.rows;
copy.cols = this.cols;
// but now I can no t directly copy mat because it is not value type so
int x;
// I assume I have clone method for List<double>
for(x=0;x<this.mat.count;x++)
{
copy.mat.Add(this.mat[x].clone());
}
// then mat is cloned
return copy; // and copy of original is returned
}
};
注意:如果您对副本(或克隆)进行任何更改,则不会影响原始对象。
我为自己做了一些扩展,可以转换未实现IClonable的项目的ICollection
static class CollectionExtensions
{
public static ICollection<T> Clone<T>(this ICollection<T> listToClone)
{
var array = new T[listToClone.Count];
listToClone.CopyTo(array,0);
return array.ToList();
}
}
我使用自动映射器来复制对象。我只是设置了一个映射,将一个对象映射到自身。您可以按自己喜欢的方式包装此操作。
对于深层复制,ICloneable是正确的解决方案,但是这是使用构造函数而不是ICloneable接口的类似于ICloneable的方法。
public class Student
{
public Student(Student student)
{
FirstName = student.FirstName;
LastName = student.LastName;
}
public string FirstName { get; set; }
public string LastName { get; set; }
}
// wherever you have the list
List<Student> students;
// and then where you want to make a copy
List<Student> copy = students.Select(s => new Student(s)).ToList();
您将需要以下库来进行复制
using System.Linq
您也可以使用for循环代替System.Linq,但是Linq使其简洁明了。同样,您可以按照其他答案的建议进行操作,并制作扩展方法等,但是这些都不是必需的。
以下代码应以最小的更改转移到列表上。
基本上,它是通过在每个连续循环中从更大范围插入一个新的随机数来工作的。如果已经存在相同或更高的数字,则将这些随机数上移一个,以使它们转移到新的更大范围的随机索引中。
// Example Usage
int[] indexes = getRandomUniqueIndexArray(selectFrom.Length, toSet.Length);
for(int i = 0; i < toSet.Length; i++)
toSet[i] = selectFrom[indexes[i]];
private int[] getRandomUniqueIndexArray(int length, int count)
{
if(count > length || count < 1 || length < 1)
return new int[0];
int[] toReturn = new int[count];
if(count == length)
{
for(int i = 0; i < toReturn.Length; i++) toReturn[i] = i;
return toReturn;
}
Random r = new Random();
int startPos = count - 1;
for(int i = startPos; i >= 0; i--)
{
int index = r.Next(length - i);
for(int j = startPos; j > i; j--)
if(toReturn[j] >= index)
toReturn[j]++;
toReturn[i] = index;
}
return toReturn;
}
另一件事:您可以使用反射。如果您可以正确地缓存它,那么它将在5.6秒内克隆1,000,000个对象(使用内部对象则为16.4秒)。
[ProtoContract(ImplicitFields = ImplicitFields.AllPublic)]
public class Person
{
...
Job JobDescription
...
}
[ProtoContract(ImplicitFields = ImplicitFields.AllPublic)]
public class Job
{...
}
private static readonly Type stringType = typeof (string);
public static class CopyFactory
{
static readonly Dictionary<Type, PropertyInfo[]> ProperyList = new Dictionary<Type, PropertyInfo[]>();
private static readonly MethodInfo CreateCopyReflectionMethod;
static CopyFactory()
{
CreateCopyReflectionMethod = typeof(CopyFactory).GetMethod("CreateCopyReflection", BindingFlags.Static | BindingFlags.Public);
}
public static T CreateCopyReflection<T>(T source) where T : new()
{
var copyInstance = new T();
var sourceType = typeof(T);
PropertyInfo[] propList;
if (ProperyList.ContainsKey(sourceType))
propList = ProperyList[sourceType];
else
{
propList = sourceType.GetProperties(BindingFlags.Public | BindingFlags.Instance);
ProperyList.Add(sourceType, propList);
}
foreach (var prop in propList)
{
var value = prop.GetValue(source, null);
prop.SetValue(copyInstance,
value != null && prop.PropertyType.IsClass && prop.PropertyType != stringType ? CreateCopyReflectionMethod.MakeGenericMethod(prop.PropertyType).Invoke(null, new object[] { value }) : value, null);
}
return copyInstance;
}
我通过使用Watcher类以一种简单的方式对其进行了测量。
var person = new Person
{
...
};
for (var i = 0; i < 1000000; i++)
{
personList.Add(person);
}
var watcher = new Stopwatch();
watcher.Start();
var copylist = personList.Select(CopyFactory.CreateCopyReflection).ToList();
watcher.Stop();
var elapsed = watcher.Elapsed;
结果:使用内部对象PersonInstance-16.4,PersonInstance = null-5.6
CopyFactory只是我的测试类,在这里我有十几个测试,包括表达式的用法。您可以在扩展程序中以其他形式实现此功能。不要忘记缓存。
我还没有测试序列化,但是我怀疑有百万级的改进。我将尝试快速的protobuf / newton。
PS:为了简化阅读,我在这里只使用自动属性。我可以使用FieldInfo进行更新,或者您应该轻松地自行实现。
我最近使用开箱即用的DeepClone功能测试了协议缓冲区序列化程序。在百万个简单对象上,它以4.2秒获胜,但是在内部对象上,它以7.4秒获胜。
Serializer.DeepClone(personList);
简介:如果您无权访问这些类,则将有所帮助。否则,它取决于对象的数量。我认为您最多可以使用10,000个对象的反射(可能会少一些),但除此之外,Protocol Buffers序列化程序的性能会更好。
有一种使用JSON序列化器和反序列化器在C#中克隆对象的简单方法。
您可以创建一个扩展类:
using Newtonsoft.Json;
static class typeExtensions
{
[Extension()]
public static T jsonCloneObject<T>(T source)
{
string json = JsonConvert.SerializeObject(source);
return JsonConvert.DeserializeObject<T>(json);
}
}
要克隆并反对:
obj clonedObj = originalObj.jsonCloneObject;