2016-06-06 2 views
0

У меня есть общий интерфейс и класс, реализующий этот интерфейс с конкретным параметром типа. У меня также есть общий класс, использующий общий интерфейс в качестве ограничения типа, но параметр типа ограничен как подкласс определенного базового класса. Я хочу указать общий класс с классом, реализующим этот интерфейс, но с проблемой преобразования класса в этот интерфейс. Следующий код иллюстрирует все классы я говорил:Варианты общих интерфейсов

базовый класс:

class DomainBase 
{ 
} 

класс, используемый в качестве параметра типа в интерфейсе

class Person : DomainBase 
{ 
} 

общий интерфейс:

public interface IRepository<T> where T : class 
{ 
    IEnumerable<T> Fetch(); 
    T Persist(T item); 
} 

Класс, реализующий общий интерфейс:

class PersonRepository : IRepository<Person> 
{ 
    public IEnumerable<Person> Fetch() 
    { 
     ... 
    } 
    public Person Persist(Person item) 
    { 
     ... 
    } 
} 

Общий класс с использованием универсального интерфейса:

class DomainBaseViewModel<Repository> 
    where Repository : IRepository<DomainBase>, new() 
{ 
    private Repository repository = new Repository(); 
    private ObservableCollection<DomainBase> items; 
} 

Однако, следующая строка не может получить скомпилированные, потому что PersonRepository не может быть преобразован в IRepository <DomainBase>:

var viewModel = new DomainBaseViewModel<PersonRepository>(); 

Хотя я могу решить эту проблему по ковариации, но она запрещает использование параметра типа в списках параметров:

public interface IRepository<out T> where T : class 
{ 
    ... 
    T Persist(object item); 
} 

class PersonRepository : IRepository<Person> 
{ 
    public Person Persist(object item) 
    { 
     ... 
    } 
} 

Поэтому мне нужно преобразовать параметр в Person, что ставит под угрозу безопасность типов.

Есть ли лучший способ разрешить ковариацию и использование параметра типа в списках параметров в этом случае?

ответ

2

Нет - вся суть ограничения ковариации в том, что она гарантирует безопасность. A PersonRepositoryне a IRepository<DomainBase>, потому что вы не можете просить его сохранить любой произвольный объект DomainBase. Что бы вы ожидали от этого кода?

class Product : DomainBase {} 
... 
IRepository<DomainBase> repository = new PersonRepository(); 
repository.Persist(new Product()); 

PersonRepository не знает, как сохранить Product значения.

Если в некоторых случаях вам нужно только «читать» части интерфейса хранилища, вы всегда можете позвонить, что в явном виде:

public interface IRepositoryReader<out T> 
{ 
    IEnumerable<T> Fetch(); 
} 

public interface IRepository<T> : IRepositoryReader<T> 
{ 
    T Persist(T item); 
} 

Тогда может быть ваш DomainBaseViewModel класс:

class DomainBaseViewModel<TRepository> 
    where TRepository : IRepositoryReader<DomainBase>, new() 
{ 
    private TRepository repository = new TRepository(); 
    private ObservableCollection<DomainBase> items; 
} 

Это не работает, если вы хотите, чтобы ваш DomainBaseViewModel также сохранял элементы.В этом случае, возможно, оно должно быть универсальным по типу модели, а также:

class DomainBaseViewModel<TRepository, TEntity> 
    where TRepository : IRepository<TEntity>, new() 
{ 
    private TRepository repository = new Repository(); 
    private ObservableCollection<TEntity> items; 
} 

Тогда:

var viewModel = new DomainBaseViewModel<PersonRepository, Person>(); 
+0

О, вы сделали большую точку, я не думал о. То, что я должен был сделать, будет классом, реализующим интерфейс с параметром типа DomainBase, например, классом DomainBaseRepository: IRepository . –

Смежные вопросы