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

有什么简单的办法吗?


当前回答

您可以使用扩展方法。

static class Extensions
{
    public static IList<T> Clone<T>(this IList<T> listToClone) where T: ICloneable
    {
        return listToClone.Select(item => (T)item.Clone()).ToList();
    }
}

其他回答

另一件事:你可以使用反射。如果你正确地缓存它,那么它将在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个对象(可能会少一点),但对于超过这个,协议缓冲区序列化器将执行得更好。

对于浅拷贝,您可以使用泛型List类的GetRange方法。

List<int> oldList = new List<int>( );
// Populate oldList...

List<int> newList = oldList.GetRange(0, oldList.Count);

引用自:generic Recipes

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;
  }
}

您还可以使用ToArray简单地将列表转换为数组,然后使用array . clone(…)克隆数组。 根据您的需要,Array类中包含的方法可以满足您的需要。

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;
}