У меня проблема с возвратной коллекцией и ковариацией, и мне было интересно, есть ли у кого-то лучшее решение.
Сценарий это:
У меня есть 2 версии реализации, и я хотел бы сохранить реализацию версии полностью отдельными (хотя у них может быть такая же логика). В реализации я хотел бы вернуть список предметов и, следовательно, в интерфейсе, я бы вернул список интерфейса элемента. Однако в фактической реализации интерфейса я хотел бы вернуть конкретный объект элемента. В коде это выглядит что-то вроде этого.
interface IItem
{
// some properties here
}
interface IResult
{
IList<IItem> Items { get; }
}
Затем будут 2 пространства имен, которые имеют бетонную реализацию этих интерфейсов. Например,
пространство имен версия1
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>();
}
}
}
будет другая реализация того же вещей под пространством имен версии2.
Чтобы создать эти объекты, будет завод, которая возьмет версию и создает соответствующий тип бетона по мере необходимости.
Если вызывающий абонент знает точную версию и делает следующее, код работает нормально
Version1.Result result = new Version1.Result();
result.Items.Add(//something);
, однако, я хотел бы, чтобы пользователь мог сделать что-то вроде этого.
IResult result = // create from factory
result.Items.Add(//something);
Но, потому что он был преобразован в другой список, добавление ничего не сделает, потому что элемент не будет добавлен обратно к исходному объекту результата.
Я могу подумать о нескольких решениях, таких как:
Я понимаю, почему это происходит (из-за типового безопасного и всех), но ни одно из обходных путей я думаю, кажется очень элегантным. У кого-нибудь есть лучшие решения или предложения?
заранее спасибо!
Обновление
После этого, я думаю, что я могу сделать следующее:
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
}
Тогда я могу иметь
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; }
}
}
таким образом, если вызывающий абонент доступа к классу результата, он пройдет через CustomCollection. Добавить (элемент TConcrete), который уже запирается. Если вызывающий абонент доступа к интерфейсу Iresult, он пройдет через CustomCollection.Add (элемент Tinterface), а проверка произойдет и убедится, что тип на самом деле TConcrete.
Я попробую посмотреть, будет ли это работать.