我在c#中有一个对象的通用列表,并希望克隆列表。列表中的项是可克隆的,但似乎没有做list. clone()的选项。

有什么简单的办法吗?


当前回答

如果你只关心值类型……

你知道这种类型:

List<int> newList = new List<int>(oldList);

如果你之前不知道类型,你需要一个helper函数:

List<T> Clone<T>(IEnumerable<T> oldList)
{
    return newList = new List<T>(oldList);
}

公正:

List<string> myNewList = Clone(myOldList);

其他回答

我使用automapper来复制一个对象。我只是设置了一个映射,将一个对象映射到它自己。您可以按照您喜欢的任何方式来完成这个操作。

http://automapper.codeplex.com/

public class CloneableList<T> : List<T>, ICloneable where T : ICloneable
{
  public object Clone()
  {
    var clone = new List<T>();
    ForEach(item => clone.Add((T)item.Clone()));
    return clone;
  }
}

如果你需要一个具有相同容量的克隆列表,你可以尝试这样做:

public static List<T> Clone<T>(this List<T> oldList)
{
    var newList = new List<T>(oldList.Capacity);
    newList.AddRange(oldList);
    return newList;
}

另一件事:你可以使用反射。如果你正确地缓存它,那么它将在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只是我的测试类,其中有十几个测试,包括表达式的使用。你可以用另一种形式在扩展中实现它。不要忘记缓存。

我还没有测试序列化,但我怀疑有一百万个类会有什么改进。我会尝试一些快速的东西。

附注:为了简化阅读,我在这里只使用了auto-property。我可以用FieldInfo进行更新,或者您应该自己轻松实现这一点。

我最近用DeepClone函数开箱测试了Protocol Buffers序列化器。它在处理一百万个简单对象时以4.2秒获胜,但在处理内部对象时,它以7.4秒的结果获胜。

Serializer.DeepClone(personList);

总结:如果您没有访问这些类的权限,那么这将有所帮助。否则,它取决于对象的计数。我认为你可以使用反射多达10,000个对象(可能会少一点),但对于超过这个,协议缓冲区序列化器将执行得更好。

要克隆一个列表,只需调用. 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 }
>