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

有什么简单的办法吗?


当前回答

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

其他回答

没有必要将类标记为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);
}

如果你已经参考了Newtonsoft。Json在你的项目和你的对象是可序列化的,你可以总是使用:

List<T> newList = JsonConvert.DeserializeObject<T>(JsonConvert.SerializeObject(listToCopy))

这可能不是最有效的方法,但除非你做了100次或1000次,否则你甚至不会注意到速度的差异。

我已经为我自己做了一些扩展,它转换了ICollection的项目,没有实现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();
    }
}

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

http://automapper.codeplex.com/

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

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