2010-03-26 30 views
6

Tôi muốn sao chép các giá trị từ một đối tượng này sang đối tượng khác. Một cái gì đó tương tự như đi qua giá trị nhưng với nhiệm vụ.C# copy constructor generator

Ví dụ:

PushPin newValPushPin = oldPushPin; //I want to break the reference here. 

tôi đã nói để viết một constructor sao chép cho việc này. Nhưng lớp này có rất nhiều thuộc tính, có thể mất một giờ để viết một hàm tạo bản sao bằng tay.

  1. Có cách nào tốt hơn để chỉ định đối tượng cho đối tượng khác theo giá trị không?
  2. Nếu không, có trình tạo bản sao của trình tạo bản sao không?

Lưu ý: ICloneable không khả dụng trong Silverlight.

+0

@Brian, lý do viết tay một hàm tạo bản sao mất quá nhiều thời gian là tôi sẽ cần phải viết các hàm tạo bản sao cho các lớp khác mà lớp chính (** PushPin **) sử dụng. –

Trả lời

6

Nếu bạn có thể đánh dấu đối tượng được sao chép dưới dạng Serializable thì bạn có thể sử dụng tuần tự hóa trong bộ nhớ để tạo bản sao. Kiểm tra mã sau, nó có lợi thế là nó cũng hoạt động trên các loại đối tượng khác và bạn không phải thay đổi hàm tạo bản sao hoặc mã sao chép mỗi lần thêm thuộc tính, xóa hoặc thay đổi:

class Program 
    { 
     static void Main(string[] args) 
     { 
      var foo = new Foo(10, "test", new Bar("Detail 1"), new Bar("Detail 2")); 

      var clonedFoo = foo.Clone(); 

      Console.WriteLine("Id {0} Bar count {1}", clonedFoo.Id, clonedFoo.Bars.Count()); 
     } 
    } 

    public static class ClonerExtensions 
    { 
     public static TObject Clone<TObject>(this TObject toClone) 
     { 
      var formatter = new BinaryFormatter(); 

      using (var memoryStream = new MemoryStream()) 
      { 
       formatter.Serialize(memoryStream, toClone); 

       memoryStream.Position = 0; 

       return (TObject) formatter.Deserialize(memoryStream); 
      } 
     } 
    } 

    [Serializable] 
    public class Foo 
    { 
     public int Id { get; private set; } 

     public string Name { get; private set; } 

     public IEnumerable<Bar> Bars { get; private set; } 

     public Foo(int id, string name, params Bar[] bars) 
     { 
      Id = id; 
      Name = name; 
      Bars = bars; 
     } 
    } 

    [Serializable] 
    public class Bar 
    { 
     public string Detail { get; private set; } 

     public Bar(string detail) 
     { 
      Detail = detail; 
     } 
    } 
+3

Điều đó sẽ không hoạt động nói chung, vì có thể sẽ có tham chiếu đến các đối tượng không nên sao chép. Làm nó bằng tay là cách duy nhất để làm cho nó đúng (bởi vì điều đó cho phép bạn xác định những gì sao chép thực sự có nghĩa là). Nếu các lớp học đủ lớn, đó là một vấn đề thực sự, chúng quá lớn. –

+0

@Donal - chưa kể có thể có các lớp được tham chiếu không thể tuần tự hóa được.Điều đó nói rằng, nếu OP có toàn quyền kiểm soát các lớp đang được sao chép, do đó được tuần tự hóa, đó chắc chắn là một giải pháp thích hợp cho họ. – Rob

+0

Hãy xem ... Tagged with Silverlight, do đó, có lẽ có một số tài liệu tham khảo cho các trường hợp của các lớp học không được kiểm soát. Sao chép thủ công là tốt hơn (viết một serializer tùy chỉnh là một thay thế khác, nhưng đó là khó hơn so với việc sao chép). –

2

Cách duy nhất (tôi biết) để thực hiện việc này và thực hiện chính xác, là tự mình thực hiện bản sao. Lấy ví dụ:

public class FrobAndState 
{ 
    public Frob Frobber { get; set;} 
    public bool State { get; set; } 
} 
public class Frob 
{ 
    public List<int> Values { get; private set; } 
    public Frob(int[] values) 
    { 
    Values = new List<int>(values); 
    } 
} 

Trong ví dụ này, bạn sẽ cần phải biết cách Frob được thực hiện, tức là thực tế là bạn cần phải gọi các nhà xây dựng để tạo ra một bản sao của nó như giá trị là chỉ đọc, để có thể tạo bản sao của một phiên bản FrobAndState đã cho.

Ngoài ra - bạn có thể không chỉ thực hiện FrobAndState.Copy thusly:

public class FrobAndState 
{ 
    // ... Properties 

    public FrobAndState Copy() 
    { 
    var new = new FrobAndState(); 
    new.State = this.State; 
    new.Frobber = this.Frobber; 
    } 
} 

Bởi vì cả hai trường hợp của FrobAndState mà bạn gọi là .Copy() trên, và các trường hợp mới cả sẽ có một tham chiếu đến cùng một trường hợp của Frobber.

Tóm lại, việc sao chép mọi thứ là cứng và mọi việc thực hiện Sao chép đều khó thực hiện.

0

Tôi muốn sao chép giá trị từ một đối tượng sang đối tượng khác. Một cái gì đó tương tự để vượt qua theo giá trị nhưng với nhiệm vụ.

Ý của bạn là gì với "có bài tập"? Nếu bạn có nghĩa là bạn muốn mọi người có thể nói:

a = b; 

Và đối với bạn để xác định những gì = phương tiện, cách duy nhất bạn có thể làm điều đó trong C# là nếu b là một loại khác nhau để a và bạn' đã xác định một chuyển đổi tiềm ẩn (hoặc nhiều hơn một cách tỉ mỉ, nếu a là viết tắt của một cái gì đó có dạng x.Y trong đó Y là một thuộc tính có setter). Bạn không thể ghi đè lên = để gán đơn giản giữa các loại giống hệt nhau trong C#.

Tôi được yêu cầu viết một hàm tạo bản sao cho việc này.Nhưng lớp này có rất nhiều thuộc tính , có thể mất giờ để viết một hàm tạo bản sao bằng tay .

Nếu điều đó thực sự đúng, thì tôi đoán bạn có vấn đề khác. Lớp học của bạn quá lớn.

0

Nếu bạn làm cho lớp học của mình Serializable bạn có thể Serialize nó thành MemoryStreamDeserialize thành một phiên bản mới.

0

Nếu bạn muốn sao chép, bạn nên sử dụng struct thay vì class. Nhưng hãy cẩn thận, thật dễ dàng để tạo ra những sai lầm tinh tế. Chúng tôi đặc biệt khuyến cáo rằng tất cả các stucts là immmutable để giảm cơ hội cho lỗi.

3

Có một thành viên bảo vệ gọi là "MemberwiseClone", bạn có thể viết những dòng này trong lớp học của bạn ...

public MyClass Clone(){ 
    return (MyClass)this.MemberwiseClone(); 
} 

sau đó bạn có thể truy cập ..

MyClass newObject = oldObject.Clone(); 
0

Mặc dù, điều này có thể không trả lời câu hỏi của bạn trực tiếp, nhưng để thêm một xu; thường là thuật ngữ Clone được liên kết với shallow copy (đối tượng được tham chiếu). Để có một bản sao sâu, tôi tin rằng bạn sẽ cần phải nhìn vào một số mô hình sáng tạo (prototype?). Câu trả lời cho this question có thể hữu ích.

0

Bạn triển khai phương pháp nhân bản vô tính của Justin Angel trong Silverlight

using System;

using System.Reflection;

bằng System.Windows;

namespace JustinAngelNet.Silverlight.Framework

{

public static class SilverlightExtensions 

{ 

    public static T Clone<T>(T source) 
    { 
     T cloned = (T) Activator.CreateInstance(source.GetType()); 

     foreach (PropertyInfo curPropInfo in source.GetType().GetProperties()) 
     { 
      if (curPropInfo.GetGetMethod() != null 
       && (curPropInfo.GetSetMethod() != null)) 
      { 
       // Handle Non-indexer properties 
       if (curPropInfo.Name != "Item") 
       { 
        // get property from source 
        object getValue = curPropInfo.GetGetMethod().Invoke(source, new object[] {}); 

        // clone if needed 
        if (getValue != null && getValue is DependencyObject) 
         getValue = Clone((DependencyObject) getValue); 

        // set property on cloned 
        if (getValue != null) 
        curPropInfo.GetSetMethod().Invoke(cloned, new object[] {getValue}); 
       } 
        // handle indexer 
       else 
       { 
        // get count for indexer 
        int numberofItemInColleciton = 
         (int) 
         curPropInfo.ReflectedType.GetProperty("Count").GetGetMethod().Invoke(source, new object[] {}); 

        // run on indexer 
        for (int i = 0; i < numberofItemInColleciton; i++) 
        { 
         // get item through Indexer 
         object getValue = curPropInfo.GetGetMethod().Invoke(source, new object[] {i}); 

         // clone if needed 
         if (getValue != null && getValue is DependencyObject) 
          getValue = Clone((DependencyObject) getValue); 
         // add item to collection 
         curPropInfo.ReflectedType.GetMethod("Add").Invoke(cloned, new object[] {getValue}); 
        } 
       } 
      } 
     } 

     return cloned; 
    } 
} 

}

Sau đó, bạn có thể làm điều này

MyClass newObject = SilverlightExtensions.Clone (oldObject);