Làm cách nào để sao chép danh sách chung trong C #?


593

Tôi có một danh sách chung các đối tượng trong C # và muốn sao chép danh sách đó. Các mục trong danh sách có thể được sao chép, nhưng dường như không có tùy chọn nào để thực hiện list.Clone().

Có một cách dễ dàng xung quanh này?


44
Bạn nên nói nếu bạn đang tìm kiếm một bản sao sâu hoặc một bản sao nông
orip

10
Bản sao sâu và nông là gì?
Đại tá Panic


3
@orip Không phải clone()theo định nghĩa là một bản sao sâu? Trong C #, bạn có thể vượt qua các con trỏ xung quanh một cách dễ dàng với =, tôi nghĩ.
Chris

13
@Chris một bản sao nông sao chép sâu hơn một cấp so với bản sao con trỏ. Ví dụ, một bản sao nông của một danh sách sẽ có cùng các yếu tố, nhưng sẽ là một danh sách khác.
orip

Câu trả lời:


385

Bạn có thể sử dụng một phương pháp mở rộng.

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

71
Tôi nghĩ List.ConvertAll có thể làm điều này trong thời gian nhanh hơn, vì nó có thể phân bổ trước toàn bộ mảng cho danh sách, thay vì phải thay đổi kích thước mọi lúc.
MichaelGG

2
@MichaelGG, nếu bạn không muốn Chuyển đổi mà chỉ sao chép / Sao chép các mục trong danh sách thì sao? Điều này sẽ làm việc? | | var cl biệtList = ListOfStrings.Convert ALL (p => p);
IbrarMumtaz

29
@IbrarMumtaz: Điều đó giống như var cl biệtList = Danh sách mới <chuỗi> (ListOfStrings);
Brandon Arnold

4
Giải pháp tốt đẹp! Bằng cách này, tôi thích Danh sách tĩnh công khai <T> CLone <T> ... Nó hữu ích hơn trong các trường hợp như thế này, vì không cần thêm diễn viên nào nữa: Danh sách <MyType> cl biệt = listToClone.Clone ();
Plutoz

2
đây là nhân bản sâu
George Birbilis

513

Nếu các yếu tố của bạn là các loại giá trị, thì bạn có thể thực hiện:

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

Tuy nhiên, nếu chúng là các loại tham chiếu và bạn muốn có một bản sao sâu (giả sử các yếu tố của bạn thực hiện đúng ICloneable), bạn có thể làm một cái gì đó như thế này:

List<ICloneable> oldList = new List<ICloneable>();
List<ICloneable> newList = new List<ICloneable>(oldList.Count);

oldList.ForEach((item) =>
    {
        newList.Add((ICloneable)item.Clone());
    });

Rõ ràng, thay thế ICloneabletrong các tổng quát ở trên và sử dụng bất kỳ loại phần tử nào của bạn ICloneable.

Nếu loại phần tử của bạn không hỗ trợ ICloneablenhưng có trình tạo bản sao, bạn có thể thực hiện việc này thay thế:

List<YourType> oldList = new List<YourType>();
List<YourType> newList = new List<YourType>(oldList.Count);

oldList.ForEach((item)=>
    {
        newList.Add(new YourType(item));
    });

Cá nhân, tôi sẽ tránh ICloneablevì cần phải đảm bảo một bản sao sâu sắc của tất cả các thành viên. Thay vào đó, tôi đề nghị xây dựng bản sao hoặc một phương thức xuất xưởng như thế YourType.CopyFrom(YourType itemToCopy)trả về một thể hiện mới của YourType.

Bất kỳ tùy chọn nào trong số này có thể được gói bằng một phương thức (phần mở rộng hoặc cách khác).


1
Tôi nghĩ rằng Danh sách <T> .Convert ALL có thể trông đẹp hơn so với việc tạo danh sách mới và thực hiện foreach + add.
MichaelGG

2
@Dimitri: Không, điều đó không đúng. Vấn đề là, khi ICloneableđược định nghĩa, định nghĩa không bao giờ cho biết bản sao sâu hay nông, vì vậy bạn không thể xác định loại hoạt động Clone nào sẽ được thực hiện khi một đối tượng thực hiện nó. Điều này có nghĩa là nếu bạn muốn làm một bản sao sâu List<T>, bạn sẽ phải làm điều đó mà không ICloneablechắc chắn đó là một bản sao sâu.
Jeff Yates

5
Tại sao không sử dụng phương thức AddRange? ( newList.AddRange(oldList.Select(i => i.Clone())hoặc newList.AddRange(oldList.Select(i => new YourType(i))
phoog

5
@phoog: Tôi nghĩ rằng nó ít đọc / dễ hiểu hơn khi quét mã, đó là tất cả. Khả năng đọc chiến thắng cho tôi.
Jeff Yates

1
@JeffYates: Một nếp nhăn không đủ cân nhắc là mọi thứ thường chỉ cần được sao chép nếu tồn tại một số đường dẫn thực thi sẽ làm biến đổi chúng. Đó là rất phổ biến để có loại không thay đổi giữ một tham chiếu đến một thể hiện của loại có thể thay đổi, nhưng không bao giờ phơi bày dụ đó để bất cứ điều gì mà sẽ biến nó. Không cần sao chép những thứ không bao giờ thay đổi đôi khi có thể là một sự tiêu tốn hiệu năng lớn , làm tăng mức sử dụng bộ nhớ theo các đơn đặt hàng lớn.
supercat

84

Đối với một bản sao nông, thay vào đó, bạn có thể sử dụng phương thức GetRange của lớp Danh sách chung.

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

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

Trích dẫn từ: Bí quyết Generics


43
Bạn cũng có thể đạt được điều này bằng cách sử dụng công cụ điều chỉnh của Danh sách <T> để chỉ định Danh sách <T> để sao chép từ đó. ví dụ: varallowClencedList = Danh sách mới <MyObject> (originalList);
Arkiliknam

9
Tôi thường sử dụng List<int> newList = oldList.ToList(). Hiệu quả tương tự. Tuy nhiên, theo tôi thì giải pháp của Arkiliknam là dễ đọc nhất.
Dan Bechard

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

Đây là một cách để làm điều đó với C # và .NET 2.0. Đối tượng của bạn yêu cầu phải được [Serializable()]. Mục tiêu là để mất tất cả các tài liệu tham khảo và xây dựng những cái mới.


11
+1 - tôi thích câu trả lời này - nó nhanh, bẩn, khó chịu và rất hiệu quả. Tôi đã sử dụng trong silverlight và đã sử dụng DataContractSerializer vì BinarySerializer không khả dụng. Ai cần viết các trang mã nhân bản đối tượng khi bạn có thể làm điều này? :)
sên

3
Tôi thích điều này. Mặc dù thật tuyệt khi làm những việc "đúng", nhanh và bẩn thường có ích.
Odrade

3
Nhanh chóng! nhưng: Tại sao bẩn?
raiserle

2
Bản sao sâu này và nhanh chóng và dễ dàng. Cẩn thận về các đề xuất khác trên trang này. Tôi đã thử một vài cái và chúng không nhân bản sâu.
Randall đến

2
Chỉ có khía cạnh tiêu cực, nếu bạn có thể gọi nó là, các lớp của bạn phải được đánh dấu Nối tiếp để nó hoạt động.
Tuukka Haapaniemi

30

Để sao chép danh sách, chỉ cần gọi .ToList (). Điều này tạo ra một bản sao nông.

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

3
Giải pháp đơn giản nhất cho đến nay
curveorzos

29
Một cảnh báo nhỏ đây là một bản sao nông ... Điều này sẽ tạo ra hai đối tượng danh sách, nhưng các đối tượng bên trong sẽ giống nhau. Tức là thay đổi một thuộc tính sẽ thay đổi cùng một đối tượng / thuộc tính trong danh sách ban đầu.
Đánh dấu G

22

Sau khi sửa đổi một chút, bạn cũng có thể sao chép:

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

Đừng quên T nên được tuần tự hóa, nếu không bạn sẽ nhận được System.R.78.Serialization.SerializationException.
Bence Végert

Câu trả lời tốt. Một gợi ý: Bạn có thể thêm if (!obj.GetType().IsSerializable) return default(T);dưới dạng câu lệnh đầu tiên ngăn chặn ngoại lệ. Và nếu bạn thay đổi nó thành một phương thức mở rộng, bạn thậm chí có thể sử dụng toán tử Elvis như var b = a?.DeepClone();( var a = new List<string>() { "a", "b" }; ví dụ được đưa ra ).
Matt

15

Trừ khi bạn cần một bản sao thực sự của mọi đối tượng trong bạn List<T>, cách tốt nhất để sao chép danh sách là tạo một danh sách mới với danh sách cũ làm tham số bộ sưu tập.

List<T> myList = ...;
List<T> cloneOfMyList = new List<T>(myList);

Thay đổi myListnhư chèn hoặc xóa sẽ không ảnh hưởngcloneOfMyList và ngược lại.

Tuy nhiên, các đối tượng thực tế mà hai Danh sách chứa vẫn giống nhau.


Tôi đồng ý với user49126, tôi thấy rằng đó là một bản sao nông và những thay đổi được thực hiện cho một danh sách được phản ánh trong danh sách khác.
Seidleroni

1
@Seidleroni, bạn sai rồi. Những thay đổi được thực hiện cho danh sách itens được đưa ra trong danh sách khác, những thay đổi trong danh sách thì không.
Wellington Zanelli

Đây là bản sao nông.
Elliot Chen

Làm thế nào đây là một bản sao nông?
mko

2
@WellingtonZanelli Chỉ cần xác nhận rằng việc xóa một phần tử khỏi myList cũng xóa nó khỏi cloneOfMyList.
Nick Gallolas

13

Sử dụng AutoMapper (hoặc bất kỳ lib ánh xạ nào bạn thích) để sao chép là đơn giản và có thể duy trì rất nhiều.

Xác định ánh xạ của bạn:

Mapper.CreateMap<YourType, YourType>();

Làm phép thuật:

YourTypeList.ConvertAll(Mapper.Map<YourType, YourType>);

13

Nếu bạn chỉ quan tâm đến các loại giá trị ...

Và bạn biết loại:

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

Nếu bạn không biết loại trước đó, bạn sẽ cần một chức năng trợ giúp:

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

Chỉ:

List<string> myNewList = Clone(myOldList);

15
Điều này không nhân bản các yếu tố.
Jeff Yates

10

Nếu bạn đã tham chiếu Newtonsoft.Json trong dự án của bạn và các đối tượng của bạn có thể tuần tự hóa, bạn luôn có thể sử dụng:

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

Có thể không phải là cách hiệu quả nhất để làm điều đó, nhưng trừ khi bạn thực hiện nó 100 lần 1000 lần, bạn thậm chí có thể không nhận thấy sự khác biệt về tốc độ.


4
Đó không phải là về sự khác biệt về tốc độ, mà là về khả năng đọc. Nếu tôi đến dòng mã này, tôi sẽ tát đầu và tự hỏi tại sao họ lại giới thiệu một thư viện bên thứ ba để tuần tự hóa và sau đó giải tuần tự hóa một đối tượng mà tôi không biết tại sao nó lại xảy ra. Ngoài ra, điều này sẽ không hoạt động cho một danh sách mô hình với các đối tượng có cấu trúc vòng tròn.
Jonathon Cwik 4/2/2015

1
Mã này đã làm việc xuất sắc cho tôi để nhân bản sâu. Ứng dụng này đang di chuyển tài liệu soạn sẵn từ Dev sang QA sang Prod. Mỗi đối tượng là một gói của một số đối tượng mẫu tài liệu và lần lượt mỗi tài liệu bao gồm một danh sách các đối tượng đoạn. Mã này cho phép tôi tuần tự hóa các đối tượng "nguồn" .NET và ngay lập tức giải tuần tự hóa chúng thành các đối tượng "đích" mới, sau đó được lưu vào cơ sở dữ liệu SQL trong một môi trường khác. Sau hàng tấn nghiên cứu, tôi đã tìm thấy rất nhiều thứ, phần lớn trong số đó quá cồng kềnh và quyết định thử nó. Cách tiếp cận ngắn và linh hoạt này là "vừa phải"!
Nhà phát

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

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

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

3

Bạn tôi Gregor Martinovic và tôi đã đưa ra giải pháp dễ dàng này bằng cách sử dụng Trình tuần tự hóa JavaScript. Không cần gắn cờ các lớp là Nối tiếp và trong các thử nghiệm của chúng tôi bằng cách sử dụng Newtonsoft JsonSerializer thậm chí còn nhanh hơn so với sử dụng BinaryFormatter. Với các phương thức mở rộng có thể sử dụng trên mọi đối tượng.

Tùy chọn .NET JavascriptSerializer tiêu chuẩn:

public static T DeepCopy<T>(this T value)
{
    JavaScriptSerializer js = new JavaScriptSerializer();

    string json = js.Serialize(value);

    return js.Deserialize<T>(json);
}

Tùy chọn nhanh hơn bằng Newtonsoft JSON :

public static T DeepCopy<T>(this T value)
{
    string json = JsonConvert.SerializeObject(value);

    return JsonConvert.DeserializeObject<T>(json);
}

2
Các thành viên riêng không được nhân bản bằng phương thức JSON. stackoverflow.com/a/78612/885627
himanshupareek66

3
 //try this
 List<string> ListCopy= new List<string>(OldList);
 //or try
 List<T> ListCopy=OldList.ToList();

3

Tôi sẽ may mắn nếu có ai từng đọc điều này ... nhưng để không trả về danh sách các đối tượng loại trong các phương thức Clone của tôi, tôi đã tạo một giao diện:

public interface IMyCloneable<T>
{
    T Clone();
}

Sau đó, tôi chỉ định phần mở rộng:

public static List<T> Clone<T>(this List<T> listToClone) where T : IMyCloneable<T>
{
    return listToClone.Select(item => (T)item.Clone()).ToList();
}

Và đây là cách triển khai giao diện trong phần mềm đánh dấu A / V của tôi. Tôi muốn phương thức Clone () của mình trả về danh sách VidMark (trong khi giao diện IClonizable muốn phương thức của tôi trả về danh sách đối tượng):

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

Và cuối cùng, việc sử dụng phần mở rộng bên trong một lớp:

private List<VidMark> _VidMarks;
private List<VidMark> _UndoVidMarks;

//Other methods instantiate and fill the lists

private void SetUndoVidMarks()
{
    _UndoVidMarks = _VidMarks.Clone();
}

Có ai thích không? Có cải tiến gì không?


2

Bạn cũng có thể chỉ cần chuyển đổi danh sách thành một mảng bằng cách sử dụng ToArray, sau đó sao chép mảng bằng cách sử dụng Array.Clone(...). Tùy thuộc vào nhu cầu của bạn, các phương thức có trong lớp Array có thể đáp ứng nhu cầu của bạn.


Điều này không hoạt động; thay đổi các giá trị trong mảng nhân bản VẪN thay đổi các giá trị trong danh sách ban đầu.
Thằn lằn Bernoulli

bạn có thể sử dụng var cl biệtList = ListOfStrings.Convert ALL (p => p); như được đưa ra bởi @IbrarMumtaz .... Hoạt động hiệu quả ... Các thay đổi trong một danh sách được giữ nguyên và không phản ánh trong một
zainul

2

Bạn có thể sử dụng phương pháp mở rộng:

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

}

Bạn có thể sao chép tất cả các đối tượng bằng cách sử dụng các thành viên loại giá trị của chúng, ví dụ, xem xét lớp này:

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

Lưu ý: nếu bạn thực hiện bất kỳ thay đổi nào trên bản sao (hoặc bản sao), nó sẽ không ảnh hưởng đến đối tượng ban đầu.


2

Nếu bạn cần một danh sách nhân bản có cùng dung lượng, bạn có thể thử điều này:

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

1

Tôi đã tạo cho riêng mình một số tiện ích mở rộng chuyển đổi ICollection của các mặt hàng không triển khai 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();
    }
}

có vẻ như một số bộ sưu tập (ví dụ: DataGrid's chọnItems tại Silverlight) bỏ qua việc triển khai CopyTo, đây là một vấn đề với cách tiếp cận này
George Birbilis

1

Tôi sử dụng automapper để sao chép một đối tượng. Tôi chỉ thiết lập một ánh xạ ánh xạ một đối tượng đến chính nó. Bạn có thể bọc thao tác này bất kỳ cách nào bạn muốn.

http://automapper.codeplex.com/


1

Sử dụng diễn viên có thể hữu ích, trong trường hợp này, cho một bản sao nông:

IList CloneList(IList list)
{
    IList result;
    result = (IList)Activator.CreateInstance(list.GetType());
    foreach (object item in list) result.Add(item);
    return result;
}

áp dụng cho danh sách chung:

List<T> Clone<T>(List<T> argument) => (List<T>)CloneList(argument);

1

Đối với một bản sao sâu, IClonizable là giải pháp chính xác, nhưng đây là một cách tiếp cận tương tự với IClonizable bằng cách sử dụng hàm tạo thay vì giao diện IClonizable.

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();

bạn sẽ cần thư viện sau nơi bạn tạo bản sao

using System.Linq

bạn cũng có thể sử dụng vòng lặp for thay vì System.Linq, nhưng Linq làm cho nó ngắn gọn và rõ ràng. Tương tự như vậy, bạn có thể làm như các câu trả lời khác đã đề xuất và thực hiện các phương pháp mở rộng, v.v., nhưng không có câu trả lời nào là cần thiết.


Đó gọi là "trình tạo bản sao". Đó là cách tiếp cận dễ bị lỗi, bất cứ khi nào bạn thêm trường mới vào Sinh viên, bạn phải nhớ thêm nó vào hàm tạo sao chép. Ý tưởng chính đằng sau "bản sao" là để tránh vấn đề đó.
kenno

2
Ngay cả với IClonizable, bạn phải có phương pháp "Clone" trong lớp. Trừ khi bạn sử dụng sự phản chiếu (mà bạn cũng có thể sử dụng theo cách tiếp cận ở trên), phương thức Clone đó sẽ trông giống như phương pháp xây dựng bản sao ở trên và sẽ gặp phải vấn đề tương tự là phải cập nhật cho các trường mới / thay đổi. Nhưng đó là câu nói "Lớp phải được cập nhật khi các trường của lớp thay đổi". Tất nhiên là có;)
ztorstri

0

Các mã sau đây nên chuyển vào một danh sách với những thay đổi tối thiểu.

Về cơ bản, nó hoạt động bằng cách chèn một số ngẫu nhiên mới từ một phạm vi lớn hơn với mỗi vòng lặp liên tiếp. Nếu đã tồn tại các số giống hoặc cao hơn số đó, hãy dịch các số ngẫu nhiên đó lên một số để chúng chuyển sang phạm vi chỉ mục ngẫu nhiên lớn hơn mới.

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

0

Một điều nữa: bạn có thể sử dụng sự phản chiếu. Nếu bạn sẽ lưu trữ bộ đệm này đúng cách, thì nó sẽ sao chép 1.000.000 đối tượng trong 5,6 giây (đáng buồn là 16,4 giây với các đối tượng bên trong).

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

Tôi đã đo nó một cách đơn giản, bằng cách sử dụng lớp 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;

KẾT QUẢ: Với đối tượng bên trong PersonInstance - 16.4, PersonInstance = null - 5.6

CopyFactory chỉ là lớp kiểm tra của tôi, nơi tôi có hàng tá bài kiểm tra bao gồm cả việc sử dụng biểu thức. Bạn có thể thực hiện điều này trong một hình thức khác trong một phần mở rộng hoặc bất cứ điều gì. Đừng quên bộ nhớ đệm.

Tôi chưa thử nghiệm tuần tự hóa, nhưng tôi nghi ngờ về sự cải tiến với một triệu lớp. Tôi sẽ thử một cái gì đó nhanh protobuf / newton.

PS: vì mục đích đọc đơn giản, tôi chỉ sử dụng tính năng tự động ở đây. Tôi có thể cập nhật với FieldInfo hoặc bạn có thể dễ dàng thực hiện việc này bằng cách riêng của mình.

Gần đây tôi đã thử nghiệm trình tuần tự hóa Bộ đệm giao thức với chức năng DeepClone. Nó chiến thắng với 4.2 giây trên một triệu vật thể đơn giản, nhưng khi nói đến vật thể bên trong, nó chiến thắng với kết quả là 7,4 giây.

Serializer.DeepClone(personList);

TÓM TẮT: Nếu bạn không có quyền truy cập vào các lớp, thì điều này sẽ giúp ích. Nếu không, nó phụ thuộc vào số lượng của các đối tượng. Tôi nghĩ rằng bạn có thể sử dụng sự phản chiếu lên tới 10.000 đối tượng (có thể ít hơn một chút), nhưng với nhiều hơn thế, bộ nối tiếp Bộ đệm Giao thức sẽ hoạt động tốt hơn.


0

Có một cách đơn giản để sao chép các đối tượng trong C # bằng cách sử dụng trình tuần tự hóa và trình giải tuần tự JSON.

Bạn có thể tạo một lớp mở rộng:

using Newtonsoft.Json;

static class typeExtensions
{
    [Extension()]
    public static T jsonCloneObject<T>(T source)
    {
    string json = JsonConvert.SerializeObject(source);
    return JsonConvert.DeserializeObject<T>(json);
    }
}

Để nhân bản và đối tượng:

obj clonedObj = originalObj.jsonCloneObject;
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.