2011-09-11 27 views
9

Tôi gặp vấn đề với việc trả lại bộ sưu tập và hiệp phương sai và tôi đã tự hỏi liệu có ai có giải pháp tốt hơn không.Làm thế nào để đối phó với hiệp phương sai khi trở về bộ sưu tập trong C#?

Kịch bản là thế này:

tôi có 2 phiên bản thực hiện và tôi muốn giữ việc thực hiện phiên bản hoàn toàn riêng biệt (mặc dù họ có thể có cùng một logic). Trong quá trình thực hiện, tôi muốn trả về một danh sách các mục và do đó trong giao diện, tôi sẽ trả về một danh sách giao diện của mục đó. Tuy nhiên, trong việc thực hiện thực tế của giao diện, tôi muốn trả lại đối tượng cụ thể của mục. Trong mã, nó trông giống như thế này.

interface IItem 
{ 
    // some properties here 
} 

interface IResult 
{ 
    IList<IItem> Items { get; } 
} 

Sau đó, sẽ có 2 không gian tên có triển khai cụ thể các giao diện này. Ví dụ,

Namespace version1

class Item : IItem 

class Result : IResult 
{ 
    public List<Item> Items 
    { 
     get { // get the list from somewhere } 
    } 

    IList<IItem> IResult.Items 
    { 
     get 
     { 
      // due to covariance, i have to convert it 
      return this.Items.ToList<IItem>(); 
     } 
    } 
} 

Sẽ có khác thực hiện điều tương tự dưới namespace Version2.

Để tạo các đối tượng này, sẽ có một nhà máy lấy phiên bản và tạo loại bê tông thích hợp nếu cần.

Nếu người gọi biết phiên bản chính xác và thực hiện những điều sau đây, mã hoạt động tốt

Version1.Result result = new Version1.Result(); 
result.Items.Add(//something); 

Tuy nhiên, tôi muốn người dùng để có thể làm điều gì đó như thế này.

IResult result = // create from factory 
result.Items.Add(//something); 

Nhưng vì nó được chuyển đổi thành danh sách khác, phần bổ sung sẽ không làm gì vì mục sẽ không được thêm trở lại đối tượng kết quả ban đầu.

tôi có thể nghĩ ra một số giải pháp như:

  1. tôi có thể đồng bộ hóa hai danh sách nhưng điều đó có vẻ là làm việc thêm để làm
  2. Return IEnumerable thay vì IList và thêm một phương pháp để tạo/xóa bộ sưu tập
  3. Tạo một bộ sưu tập tùy chỉnh mà mất TConcrete và TInterface

tôi hiểu tại sao điều này đang xảy ra (do sự an toàn và tất cả các loại), nhưng không ai trong số workaroun Tôi nghĩ có vẻ rất tao nhã. Không ai có giải pháp hay đề xuất tốt hơn?

Cảm ơn trước!

Cập nhật

Sau khi suy nghĩ về nhiều điều này, tôi nghĩ mình có thể làm như sau:

public interface ICustomCollection<TInterface> : ICollection<TInterface> 
{ 
} 

public class CustomCollection<TConcrete, TInterface> : ICustomCollection<TInterface> where TConcrete : class, TInterface 
{ 
    public void Add(TConcrete item) 
    { 
     // do add 
    } 

    void ICustomCollection<TInterface>.Add(TInterface item) 
    { 
     // validate that item is TConcrete and add to the collection. 
     // otherwise throw exception indicating that the add is not allowed due to incompatible type 
    } 

    // rest of the implementation 
} 

sau đó tôi có thể có

interface IResult 
{ 
    ICustomCollection<IItem> Items { get; } 
} 

then for implementation, I will have 

class Result : IResult 
{ 
    public CustomCollection<Item, IItem> Items { get; } 

    ICustomCollection<TItem> IResult.Items 
    { 
     get { return this.Items; } 
    } 
} 

theo cách đó, nếu người gọi là truy cập vào lớp Result, nó sẽ đi qua CustomCollection.Add (mục TConcrete) đã được TConcrete.Nếu người gọi đang truy cập thông qua giao diện IResult, nó sẽ đi qua customCollection.Add (mục TInterface) và xác thực sẽ xảy ra và đảm bảo rằng loại thực sự là TConcrete.

Tôi sẽ thử và xem điều này có hiệu quả hay không.

+1

Tôi nghĩ rằng với tùy chọn # 2 sẽ yêu cầu số lượng mã ít nhất. Nó cũng sẽ làm giảm diện tích bề mặt của giao diện của bạn, vì việc triển khai sẽ không chịu trách nhiệm cho sự hỗ trợ đầy đủ của IList mà người gọi có thể không cần. –

+0

Giải pháp của bạn có vẻ tốt, nhưng tại sao bạn sử dụng 'ICustomCollection 'và không phải' ICollection ' trực tiếp? – svick

+0

Tôi hoàn toàn có thể.Lý do duy nhất là tôi có một vài phương pháp đặc biệt cho colleciton không có sẵn cho ICollection thông thường cần được truy cập bằng mã nội bộ, mà tôi quên đề cập đến. – Khronos

Trả lời

2

Sự cố bạn đang gặp phải là vì muốn hiển thị loại (nói trong số những thứ khác) “bạn có thể thêm bất kỳ IItem nào vào tôi”, nhưng thực tế là “Bạn chỉ có thể thêm Item s cho tôi” . Tôi nghĩ giải pháp tốt nhất là thực sự phơi bày IList<Item>. Các mã sử dụng này sẽ phải biết về bê tông Item anyway, nếu nó nên thêm chúng vào danh sách.

Nhưng nếu bạn thực sự muốn làm điều này, tôi nghĩ rằng giải pháp sạch sẽ là 3., nếu tôi hiểu bạn một cách chính xác. Nó sẽ là một trình bao bọc xung quanh IList<TConcrete> và nó sẽ thực hiện IList<TInterface>. Nếu bạn cố gắng đặt thứ gì đó vào đó không phải là TConcrete, nó sẽ ném một ngoại lệ.

0

+1 nhận xét của Brent: trả lại bộ sưu tập không thể sửa đổi và cung cấp các phương pháp sửa đổi trên lớp học.

Chỉ cần nhắc lại lý do tại sao bạn không thể có được 1 làm việc theo cách giải thích được:

Nếu bạn cố gắng thêm để List<Item> yếu tố mà chỉ đơn giản thực hiện IItem nhưng không phải là loại (hoặc có nguồn gốc từ) Mục bạn sẽ không có thể lưu trữ mục mới này trong danh sách. Do hành vi của giao diện kết quả sẽ rất không nhất quán - một số yếu tố triển khai IItem có thể được thêm vào tốt, sime sẽ thất bại, và khi bạn thay đổi triển khai thành hành vi version2 cũng sẽ bị cahnge.

Sửa chữa đơn giản sẽ là lưu trữ IList<IItem> insitead của List<Item>, nhưng phơi bày colection trực tiếp yêu cầu suy nghĩ cẩn thận.

0

Vấn đề là Microsoft sử dụng một giao diện, IList <T>, cho cả hai mô tả cả hai bộ sưu tập có thể được nối thêm và bộ sưu tập không thể. Trong khi về mặt lý thuyết có thể cho một lớp thực hiện IList <Cat> trong thời trang có thể thay đổi và cũng thực hiện IL < Động vật > trong thời trang không thay đổi (thuộc tính ReadOnly của giao diện cũ sẽ trả về false, và sau này sẽ trả về true), không có cách nào cho một lớp để chỉ định rằng nó triển khai IList <Cat> một cách và IList <T> nơi mèo: T, một cách khác. Tôi muốn Microsoft đã thực hiện IList <T> Danh sách <T> thực hiện IReadableByIndex < ra T >, IWritableByIndex < trong T >, IReadWriteByIndex <T>, IAppendable < trong T >, và ICountable, vì những sẽ cho phép hiệp phương sai và contravariance, nhưng họ thì không. Có thể hữu ích khi tự triển khai các giao diện như vậy và xác định trình bao bọc cho chúng, tùy thuộc vào mức độ hiệp phương sai nào sẽ hữu ích.

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