2011-12-25 47 views
14

Tôi có một lớp cơ sởThừa kế C#. lớp được thừa kế từ lớp cơ sở

public class A 
{ 
    public string s1; 
    public string s2; 
} 

Tôi cũng có một lớp có nguồn gốc:

public class B : A 
{ 
    public string s3; 
} 

Giả sử chương trình của tôi tạo ra một thể hiện của lớp A.

A aClassInstance = new A(); 

một số thông số đã được đặt:

aClassInstance.s1 = "string 1"; 
aClassInstance.s2 = "string 2"; 

Tại thời điểm này, tôi muốn tạo một thể hiện của lớp B. Nhưng tôi muốn B đã có các giá trị thẩm của tôi về lớp A.

này đã không làm việc:

public B bClassInstance = new B(): 
bClassInstance = (B)aClassInstance; 

cẢ DID NÀY:

Made một phương pháp nhân bản trong lớp A.

public B cloneA() {  
    A a = new A(); 
    a = (A)this.MemberwiseClone() 
    return(B)a; 
} 

mã VS mất cả những điều trên - bu t tôi nhận được lỗi run-time

Xin giúp

+2

Hãy cẩn thận khi nhân bản - đặc biệt nếu lớp của bạn có các trường tham chiếu có thể thay đổi. Quyết định xem bạn có muốn bản sao sâu hay bản sao nông và ghi lại nó hay không. – TrueWill

+1

đúng. Lớp đặc biệt này không có tham chiếu, do đó một bản sao nông hoạt động cho nó. Tôi đã tìm thấy một bài đăng tốt về nông và nhân bản sâu ở đây cho bất kỳ ai quan tâm: http://itpksingh.blogspot.com/2009/08/shallow-copyingdeep-copyingobject.html – Sam

+0

Tìm thấy một giải pháp sử dụng ValueInjector. StackOverFlow không cho phép tôi "trả lời câu hỏi của riêng tôi". Khi đó, sẽ đăng chi tiết đầy đủ. – Sam

Trả lời

21

Vấn đề cơ bản bạn có là, bạn phải xây dựng một thể hiện kiểu B (có chứa nguyên nhân thuộc tính loại A). Cách tiếp cận của bạn để sao chép một cá thể A sẽ không hoạt động, bởi vì nó cung cấp cho bạn một thể hiện kiểu A, mà bạn không thể chuyển đổi thành B.

Tôi sẽ viết các hàm tạo cho lớp A và B lấy tham số kiểu A. Hàm tạo của lớp B chỉ chuyển giá trị cho lớp cơ sở A.Các constructor của lớp A biết làm thế nào để sao chép các lĩnh vực với bản thân:

class A { 
    public A(A copyMe) { 
     s1 = copyMe.s1; 
     ... 
    } 

class B : A { 

    public B(A aInstance) : base(aInstance) { 
    } 

} 

Sử dụng nó theo cách này:

A a = new A(); 
a.s1 = "..."; 

B b = new B(a); 

EDIT

Khi bạn không muốn phải thay đổi hàm tạo của A khi thêm các trường hoặc đạo cụ mới, bạn có thể sử dụng sự phản chiếu để sao chép các thuộc tính. Hoặc sử dụng một thuộc tính tùy chỉnh để trang trí những gì bạn muốn sao chép, hoặc sao chép chỉ tất cả các đạo cụ/lĩnh vực A:

public A (A copyMe) { 
    Type t = copyMe.GetType(); 
    foreach (FieldInfo fieldInf in t.GetFields()) 
    { 
     fieldInf.SetValue(this, fieldInf.GetValue(copyMe)); 
    } 
    foreach (PropertyInfo propInf in t.GetProperties()) 
    { 
     propInf.SetValue(this, propInf.GetValue(copyMe)); 
    } 
} 

tôi havn't thử đoạn code, nhưng điểm sẽ trở nên rõ ràng.

+0

Tôi đang cố gắng nâng cấp để mỗi thành viên không phải sao chép riêng lẻ: lớp A { công khai A (A copyMe) { s1 = copyMe.s1; (A) copyMe.MemberwiseClone() sẽ trả về bản sao nông của cuộc gọi A của tôi mà tôi cần. Bây giờ tất cả những gì chúng ta cần làm là truyền lại cho B constructor ... câu hỏi là làm thế nào. tiếc là chúng tôi không thể làm điều này: Class A { công A (Một copyMe) { này = (A) copyMe.MemberwiseClone() }} ... } – Sam

+1

@Sam: Vấn đề là, bạn phải xây dựng một đối tượng kiểu B. Bởi vì A là một phần của B, bạn phải tìm cách cài đặt các thuộc tính của cá thể B mới, thuộc về loại A. Your * clone A * cách tiếp cận sẽ không hiệu quả vì nó cung cấp cho bạn một cá thể loại A, nơi bạn cần một thể hiện kiểu B! Những gì bạn có thể làm, là sử dụng sự phản chiếu trong constructor của A. Tôi sẽ cập nhật câu trả lời của tôi để phản ánh điều đó. – Jan

8

Bạn có thể tạo ra một phương pháp nhân bản chung trong lớp A:

 public T Clone<T>() where T : A, new() { 
      return new T() { a = this.a, b = this.b}; 
    } 

Hoặc nếu bạn muốn làm cho nhân bản mở rộng:

 public T Clone<T>() where T : A, new() { 
      var result = new T(); 
      this.CopyTo(result); 
      return result; 
    } 

    protected virtual void CopyTo(A other) { 
      other.a = this.a; 
      other.b = this.b; 
    } 

Bạn sử dụng nó như sau:

 A a = new A(); 
    // do stuff with a 
    // Create a B based on A: 
    B b = a.Clone<B>(); 

Xin lưu ý: trong ví dụ của bạn, cả A() và MemberwiseClone mới sẽ tạo đối tượng mới loại A.

Nếu bạn không muốn tự mình viết mã sao chép, bạn có thể xem công cụ như AutoMapper.

+0

Cảm ơn! Trong ví dụ này, tôi có phải đặt tất cả các thành viên (như a = this.a, b = this.b, v.v.) không. Nếu lớp học có nhiều thành viên, dù sao để tránh mã hóa từng thành viên? (nếu tôi quyết định thêm thành viên khác, tôi cũng phải cài đặt nó trong chức năng nhân bản) – Sam

+0

Bạn có thể xây dựng thứ gì đó với sự phản chiếu (vòng lặp mặc dù tất cả các trường và sao chép chúng), nhưng từ kinh nghiệm tôi biết, bạn nên kiểm soát việc sao chép chính mình. Đôi khi bạn cần một bản sao nông, đối với một số đối tượng bạn cần một bản sao sâu. – GvS

+0

Tại sao không khác = (A) này.MemberwiseClone(); 'làm việc i nthe ở trên (thay vì phải thiết lập mỗi thuộc tính). Tôi đang cố gắng tránh đặt từng thuộc tính hoặc sử dụng sự phản chiếu. – PeterX

1

Sau khi chơi xung quanh và đọc mọi thứ tôi có thể nhìn thấy, cả hai giải pháp trên đều do GvS và Jan làm việc. Tuy nhiên, kết quả cuối cùng mà tôi muốn đạt được không phải là bắt buộc phải viết ra từng thành viên trong các phương pháp sao chép.

Lý do: a) Nếu lớp được chỉnh sửa và đối tượng khác được thêm vào, phương pháp sao chép sẽ phải được cập nhật. Nếu ai đó cập nhật lớp học, họ có thể quên làm điều này.

b) Có thể có nhiều thành viên và việc chỉ định chúng có thể tốn thời gian.

c) Nó không cảm thấy đúng. (Có lẽ vì tôi rất lười).

May mắn thay, tôi không phải là người duy nhất có cùng suy nghĩ. Tìm thấy một giải pháp rất dễ dàng thông qua ValueInjector. (nó đã được thảo luận trên các bảng này rất nhiều).

Sau khi nhận được các dll (http://valueinjecter.codeplex.com/documentation)

Mã này trở thành:

A a = new A(); 
a.s1 = "..."; 


B b = new B(); 
b.InjectFrom(a); 

Đó là nó :)

Rõ ràng bạn sẽ phải bao gồm :

using Omu.ValueInjecter; 

Và đừng quên thêm nó vào tài liệu tham khảo.

Các vấn đề liên quan