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ư:
- 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
- Return IEnumerable thay vì IList và thêm một phương pháp để tạo/xóa bộ sưu tập
- 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.
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. –
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
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