2012-03-15 14 views
5

Tôi có tờ khai giao diện sau:Làm thế nào để tôi tham khảo một phụ huynh từ một đối tượng con với các giao diện chung trong C#?

interface IOrder<T> where T: IOrderItem 
{ 
    IList<T> Items { get; set; } 
} 

interface IOrderItem 
{ 
    IOrder<IOrderItem> Parent { get; set; } // What do I put here? 
} 

Tôi muốn các mục trong danh sách để có một tham chiếu đến đối tượng tiêu đề, vì vậy nó có thể sử dụng ID và các lĩnh vực khác từ tiêu đề.

Trong các lớp cụ thể của tôi, nó phàn nàn rằng tôi không triển khai đúng "Cha mẹ".

class StoreOrder : IOrder<StoreOrderItem> 
{ 
    public IList<StoreOrderItem> Items { get; set; } 
} 

class StoreOrderItem : IOrderItem 
{  
    public StoreOrder Parent { get; set; } // This doesn't satisfy the interface 
} 

tôi đã cố gắng thiết lập IOrderItem như IOrderItem<T> và đi qua trong các loại phụ huynh, nhưng điều đó dẫn đến tham chiếu vòng tròn kể từ khi lớp header requries loại hàng lớp ... Tôi đã nhầm lẫn.

Bất kỳ lời khuyên nào về cách triển khai đúng cách này?

+0

[Điều này có thể thú vị đối với bạn] (http://msdn.microsoft.com/en-us/library/dd469487.aspx) – abatishchev

Trả lời

3

Nếu bạn xác định giao diện của mình như sau:

interface IOrder<T> where T : IOrderItem<T> 
{ 
    IList<T> Items { get; set; } 
} 
interface IOrderItem<T> where T : IOrderItem<T> 
{ 
    IOrder<T> Parent { get; set; } 
} 

Sau đó bạn có thể thực hiện chúng như thế này để có được những chức năng mà bạn mong đợi:

class StoreOrder : IOrder<StoreOrderItem> 
{ 
    public IList<StoreOrderItem> Items { get; set; } 
} 
class StoreOrderItem: IOrderItem<StoreOrderItem> 
{ 
    public IOrder<StoreOrderItem> Parent { get; set; } 
} 
+0

Điều này sửa lỗi trình biên dịch, nhưng loại bỏ thuộc tính 'Parent' trở về' IOrder 'thay vì' StoreOrder' giống như nó xuất hiện người hỏi đang cố gắng. Vì vậy, nếu bạn có một 'StoreOrderItem' và muốn làm bất cứ điều gì với' Parent' bên cạnh hoạt động trên thuộc tính 'IOrder .Items' bạn sẽ phải đưa' Parent' vào 'StoreOrder' trước. – devgeezer

+2

@devgeezer - Bạn đúng. Tuy nhiên, việc triển khai của tôi sẽ cho phép bất kỳ triển khai 'IOrder ' nào làm cha mẹ. Bất cứ điều gì mà 'IOrderItem ' cần biết về cha mẹ của nó nên được thực hiện trong giao diện 'IOrder ', vì mối quan hệ giữa hai được xác định ở cấp độ giao diện chứ không phải cấp thực hiện. –

+0

Điểm tuyệt vời. Tôi nghĩ rằng tôi đã đọc quá nhiều ý định vào câu hỏi. +1 – devgeezer

1
class StoreOrder : IOrder<StoreOrderItem> 
{ 
    public int Id { get; set; } 
} 

class StoreOrderItem : IOrderItem 
{  
    public IOrder<IOrderItem> Parent { get; set; } // This doesn't satisfy the interface 
} 

Bạn không thể chuyên - IOrder<IOrderItem> tổng quát hơn StoreOrder

+0

Tại sao chọn ID? Tại sao không có 'IOrder .ILIST '? – abatishchev

+0

Bạn nói đúng, không biết tại sao. Đã chỉnh sửa. – IronicMuffin

+0

@Eugen: Mã của bạn có thể biên dịch được không? – abatishchev

0

Tuyên bố để đáp ứng các giao diện:

class StoreOrder : IOrder<StoreOrderItem> 
{ 
    // interface members 
    public IList<StoreOrderItem> Items { get; set; } 

    // own members 
    public int Id { get; set; } 
} 

class StoreOrderItem : IOrderItem 
{ 
    public IOrder<IOrderItem> Parent { get; set; } 
} 

Để truy cập các thành viên tùy chỉnh, bạn sẽ phải bỏ:

class StoreOrderItem : IOrderItem 
{ 
    void Test() 
    { 
     int id = ((StoreOrder)this.Parent).ID; 
    } 
} 
1

Dưới đây là một giải pháp cho việc thay đổi giao diện:

interface IOrder<TOrder, TOrderItem> 
    where TOrderItem : IOrderItem<TOrder> 
{ 
    IList<TOrderItem> Items { get; set; } 
} 

interface IOrderItem<TOrder> 
{ 
    TOrder Parent { get; set; } 
} 

Thực hiện thay đổi để StoreOrderStoreOrderItem để hỗ trợ thay đổi giao diện thêm một vài thuộc tính vào mỗi thử nghiệm sau:

class StoreOrder: IOrder<StoreOrder, StoreOrderItem> 
{ 
    public DateTime Date { get; set; } 
    public IList<StoreOrderItem> Items { get; set; } 
} 

class StoreOrderItem : IOrderItem<StoreOrder> 
{ 
    public string ItemName { get; set; } 
    public decimal ItemPrice { get; set; } 
    public StoreOrder Parent { get; set; } 
} 

... và bây giờ tạo StoreOrderStoreOrderItem trường hợp, và đưa chúng thông qua các bước của họ:

void Main() 
{ 
    var so = new StoreOrder { Date = DateTime.Now }; 
    var item = new StoreOrderItem { 
      Parent = so, 
      ItemName = "Hand soap", 
      ItemPrice = 2.50m }; 
    so.Items = new [] { item }; 

    Console.WriteLine(item.Parent.Date); 
    Console.WriteLine(so.Items.First().ItemName); 
} 

... khi chạy, in:

3/16/2012 10:43:55 AM 
Hand soap 

lựa chọn khác là để loại bỏ phần trên và lấy this solution và thay đổi nó bằng cách thêm thuộc tính Gốc với loại mong muốn và sử dụng triển khai giao diện rõ ràng để tránh truyền tại các trang gọi, làm cho một cái gì đó StoreOrderItem thực hiện như thế này:

class StoreOrderItem : IOrderItem 
{ 
    public string ItemName { get; set; } 
    public decimal ItemPrice { get; set; } 
    public StoreOrder Parent { get; set; } // note: original implementation 

    IOrder<IOrderItem> IOrderItem.Parent { // explicit interface implementation 
     get { return (IOrder<IOrderItem>)this.Parent; } 
     set { this.Parent = (StoreOrder)value; } 
    } 
} 

ưa thích của tôi ở trên là đề xuất đầu tiên ở trên với các thông số hai tổng quát để IOrder và không bị giới hạn generic-tham số trên IOrderItem. Một phiên bản trước đó mà tôi đã đăng và hiện đã chỉnh sửa có cả hai giao diện, mỗi giao diện đều có hai loại chung giống nhau với cùng một ràng buộc.Tôi cảm thấy như thế này đã được một chút overboard vì vậy tôi pared nó trở lại thực hiện ở trên. Mặc dù thiếu hoàn toàn các ràng buộc đối với thông số loại TOrder thành IOrderItem - các nỗ lực nhằm giả mạo các loại khác tại vị trí của nó (ví dụ: object) dẫn đến lỗi biên dịch. Sử dụng TOrder thay vì chỉ gọi số T cung cấp gợi ý về loại được mong đợi khi không có ràng buộc loại. Đó sẽ là bản chỉnh sửa cuối cùng của tôi - tôi cảm thấy nó là ngắn gọn nhất trong nỗ lực của tôi; nếu bạn tò mò, tôi có thể cung cấp việc triển khai thực hiện trước đó có các kiểu đôi-generic-constrained trên các giao diện, nhưng đây là ít nhất là giải pháp ưa thích của tôi. chúc mừng!

+0

Tôi đã học được rằng giải pháp đầu tiên của tôi có tên: "Mẫu định kỳ tò mò định kỳ". SO tag [ctrp]. http://en.wikipedia.org/wiki/Curiously_recurring_template_pattern – devgeezer

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