我在C#中有一个通用对象列表,并希望克隆该列表。列表中的项是可克隆的,但似乎没有list.Clone()
选项。
这个问题有简单的解决方法吗?
我在C#中有一个通用对象列表,并希望克隆该列表。列表中的项是可克隆的,但似乎没有list.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
}
};
List<T>.ConvertAll(Converter<T, T>)
方法创建一个新列表,其中包含原始列表的所有元素,并使用返回输入值的转换函数。List<int> originalList = new List<int> { 1, 2, 3, 4, 5 };
List<int> clonedList = new List<int>(originalList.ConvertAll(x => x));
另外一件事:您可以使用反射。如果您正确地缓存它,那么它将在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。
P.S.:为了阅读简便起见,我只在此处使用了自动属性。我可以使用FieldInfo进行更新,或者您可以轻松地自己实现此功能。
最近我使用Protocol Buffers序列化器对DeepClone函数进行了测试。它在一百万个简单对象上获胜,用时4.2秒,但当涉及到内部对象时,它的结果为7.4秒。
Serializer.DeepClone(personList);
摘要:如果您无法访问这些类,则此方法将有所帮助。否则,它取决于对象的数量。我认为您可以使用反射来处理多达10,000个对象(可能略少一些),但对于更多的对象,Protocol Buffers序列化程序将表现更好。
我为自己制作了一些扩展,可以将未实现IClonable接口的项目集合进行转换。
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();
}
}
ToArray
将列表转换为数组,然后使用 Array.Clone(...)
克隆数组。根据您的需要,Array 类中包含的方法可能能够满足您的需求。在C#中,使用JSON序列化器和反序列化器有一种简单的方法来克隆对象。
您可以创建一个扩展类:
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;
// 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;
}
对于深度克隆,我使用反射如下:
public List<T> CloneList<T>(IEnumerable<T> listToClone) {
Type listType = listToClone.GetType();
Type elementType = listType.GetGenericArguments()[0];
List<T> listCopy = new List<T>();
foreach (T item in listToClone) {
object itemCopy = Activator.CreateInstance(elementType);
foreach (PropertyInfo property in elementType.GetProperties()) {
elementType.GetProperty(property.Name).SetValue(itemCopy, property.GetValue(item));
}
listCopy.Add((T)itemCopy);
}
return listCopy;
}
您可以互换使用List或IEnumerable。