Наследование интерфейсов в C#

Я пытаюсь решить довольно большую (для меня) проблему, с которой столкнулся при написании своего приложения. Посмотрите на это, пожалуйста (я постараюсь сократить код для простоты):

У меня есть корневой интерфейс с именем IRepository<T>. Далее, IBookRepository : IRepository<Book> Далее, конкретный класс, который его реализует: BookRepository : IBookRepository

В классе RepositoryManager я объявил private IRepository<IRepoItem> currentRepo;

IRepoItem — это интерфейс, реализованный классом Book.

Теперь, когда я пытаюсь сделать что-то вроде этого:

currentRepo = new BookRepository();

VisualStudio выдает сообщение об ошибке:

Невозможно неявно преобразовать тип BookRepository в IRepository. Существует явное преобразование (вам не хватает приведения?)

Я пытался выполнить явное приведение, но выдается исключение во время выполнения...

Я знаю (почти точно), что это то, что называется ковариацией, и это (вероятно) решено в .Net 4.0. К сожалению, я пишу с использованием фреймворка версии 3.5 и не могу это изменить. Пожалуйста, дайте мне несколько советов, что делать - как обойти эту проблему? Я хотел бы получить currentRepo от RepoFactory, который будет создавать несколько видов репозиториев в зависимости от потребностей пользователя. Я не знаю, разрешены ли здесь ссылки, но я веду какой-то блог на http://olgatherer.wordpress.com/ где я описываю создание приложения. Мой английский не очень хорош, но я надеюсь, что этого достаточно, чтобы понять меня. Заранее спасибо за ответ.

С уважением, skrzeczowas


person skrzeczowas    schedule 06.10.2010    source источник
comment
Каков объявленный тип currentRepo?   -  person John Bledsoe    schedule 06.10.2010
comment
Неважно, я вижу, что это IRepository‹IRepoItem›.   -  person John Bledsoe    schedule 06.10.2010


Ответы (3)


В .NET 3.5 вы определенно не можете рассматривать IRepository<Book> как IRepository<IRepoItem>.

Нам нужно больше узнать о том, для чего вы используете репозиторий в RepositoryManager, чтобы действительно понять, как это решить... но не могли бы вы создать нестандартный IRepository интерфейс, который IRepository<T> расширяет? Включите в него все члены, которые не относятся к T. Затем вы можете объявить currentRepo просто как IRepository.

person Jon Skeet    schedule 06.10.2010
comment
+1 - я думал об этом нестандартном интерфейсе IRepository, но не знал, как его реализовать. Отличный Джон! знак равно - person Will Marcouiller; 06.10.2010
comment
Привет еще раз! У меня есть репозитории, как описано здесь: olgatherer.wordpress.com/2010/08/ 18/repositoriesrus И я создал DbManager для управления базой данных и создания репозиториев для каждой из таблиц (книги, комиксы, постштампы и т.д.). Теперь я хотел бы, чтобы currentRepository в DbManager мог быть BookRepository, StampsRepository, чем угодно — я думал, что эта штука с интерфейсами будет в порядке, но, к сожалению, это не так, потому что IRepoItem (как я понял сегодня) должен иметь все другие свойства тогда... :( - person skrzeczowas; 07.10.2010

Попробуйте использовать промежуточный слой (IRep):

    interface IRepository<T>
    {
    }

    interface IRep<T> : IRepository<IRepoItem> where T : IRepoItem
    {
    }

    interface IBookRepository : IRep<Book>
    {
    }

    class BookRepository : IBookRepository
    {
    }

тогда вы можете делать то, что хотите:

        BookRepository br = new BookRepository();
        IRepository<IRepoItem> currentRepo = br;
person Grozz    schedule 06.10.2010
comment
Привет! Это здорово и очень элегантно, я очень рад, что вы опубликовали это. К сожалению, как я описал в комментарии к посту выше, я понял, что это меня не полностью устраивает. IRepoItem — это интерфейс, который будет реализован в книгах, марках, комиксах, поэтому он должен иметь все свойства для разных предметов. Это неразумно, и мне нужно найти другой способ добиться того, чего я хочу - это означает, что я хочу, чтобы currentRepo был BookRepository или StampsRepository или каким-либо другим хранилищем... Может быть, кто-нибудь поможет, пожалуйста? - person skrzeczowas; 07.10.2010
comment
IRepoItem, являющийся интерфейсом для других вещей, должен приводить только к объединению того, что у них есть общего, а не всех свойств. Например, IMoveable должен предоставлять только метод Move, а не все, что есть у всех производных от него классов (таких как Cheetah, Car, Airplane). - person Grozz; 07.10.2010
comment
Но тогда, если у меня, допустим, будет currentRepo = StampsRepo();, я смогу использовать только те методы, из которых состоит IRepository. Я не смогу использовать методы IStampsRepository :( Я полагаю, что тогда я могу спросить, какого типа этот currentRepo: if(currentRepo — это IBookRepository) сделать что-то еще, если (currentRepo — это IStampsRepository) сделать что-то… Но это не хорошее объектное программирование, я думаю. Я должен пересмотреть все сделанные мной предположения, потому что, возможно, то, что я хочу, глупо и недостижимо... - person skrzeczowas; 07.10.2010
comment
Удачи в рефакторинге, если это так;) - person Grozz; 07.10.2010

В .NET 3.5 нет никакой связи между IRepository и IRepository в .NET 4, вы можете добиться чего-то подобного с поддержкой ковариантности и контравариантности (но это зависит от объявления интерфейса IRepository)

A рекомендовал бы использовать не универсальный интерфейс IRepository и выполнять приведение самостоятельно. Я знаю, это не замечательно, но описанная вами функциональность требует поддержки ковариантности/контравариантности.

person Eilistraee    schedule 06.10.2010
comment
Спасибо за этот ответ, я использовал этот ниже и понял, что это меня не полностью устраивает :( Я не знаю, что теперь делать ... IRepoItem не может иметь все свойства, из которых состоят книги, комиксы, марки :( - person skrzeczowas; 07.10.2010