Возвращая интерфейс, но бетон может иметь свойства не на интерфейсе, могу ли я получить их с помощью приведения?

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

Поэтому я объясню проблему, которую пытаюсь решить, и, возможно, кто-то может указать мне правильное направление.

Я создаю приложение, которое возвращает страницу для любого запроса, у меня есть три типа страниц, Cms, Product и Category.

Все три должны реализовать следующий интерфейс:

public interface IPage
    {
        PageType PageType { get; set; }
        PageContent Content { get; set; }
        Meta Meta { get; set; }
    }

Эти свойства запрашиваются независимо от типа страницы.

страница может иметь дополнительные свойства в зависимости от их типа, например страница категории может выглядеть так:

public class CategoryPage : IPage
    {
        public PageType PageType { get; set; }
        public PageContent Content { get; set; }
        public Meta Meta { get; set; }

        public List<Product> Products { get; set; }
    }

На данный момент у меня есть служба страниц, которая вернет страницу для запрошенного URL.

На основе PageType он знает, какой тип страницы возвращать.

Проблема в том, что pageService возвращает IPage, чтобы он мог вернуть любой из типов страниц.

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

Но есть ли способ, которым я могу вернуть общий тип страницы и дать получателю знать, что это такое?

Я уверен, что, как я делаю это, что момент не самый лучший способ и хотел бы получить некоторые указания и советы о том, как я могу решить эту маленькую проблему.

Спасибо

Обновить

Я согласился на актерский состав.

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

Или, может быть, то, что я пытаюсь сделать здесь, просто неправильно, и мне нужен другой подход. Я думаю, что буду настаивать на том, что у меня есть сейчас, но продолжаю думать об этом.

Обновление 2

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

Это в сочетании с Ipage, который наследует все необходимое, кажется достаточно хорошим решением и устраняет необходимость в приведении.

4 ответа

Решение

Вы всегда можете проверить, принадлежит ли объект, на который вы ссылаетесь, определенному типу, используя ключевое слово is;

if(obj is Class1) {

Тем не менее, если ваш дизайн требует, чтобы вы знали конкретные типы, скорее всего, что-то не так с самим дизайном. Если существуют различия в поведении между классами, реализуйте различия внутри классов вместо того, чтобы приводить их к реализации вне их.

Если ваш приемник должен знать конкретный тип, значит, вы не используете интерфейсы должным образом.

Если вы возвращаете страницу, на самом деле нет никаких оснований знать, что это за страница. Я бы добавил Render метод к IPage интерфейс, так что все, что нужно сделать получателю, это вызвать Render() и страница будет обрабатывать остальное.

Вот два способа проверить, что ваш интерфейс, возвращаемый службой, является вашим конкретным типом.

        IPage page;
        if (page is CategoryPage)
        {
           // use type here
        }

        CategoryPage categoryPage = page as CategoryPage;
        if (categoryPage != null)
        {
          // use type here
        }

В идеале, почти никогда не нужно использовать типы типов, особенно учитывая наличие дженериков. Однако с прагматической точки зрения иногда лучше использовать типы типов, чем идти на огромные усилия, чтобы их избежать.

В некотором смысле, это не элегантно иметь коллекцию объектов, которые имеют различные способности (или фабричный метод, который будет возвращать различные способности) в тех случаях, когда может быть необходимо использовать способности, которые существуют в одних объектах, но не в других. Конечно, можно сказать что-то вроде

  IInterfaceThatMayOrMayNotBePresent foo = bar as IInterfaceThatMayOrmayNotBePresent;
  if (foo!= null)
    foo.MethodOfThatInterface();

но это немного запах кода. Часто полезно определить, почему нужно использовать методы, которых нет во всех экземплярах списка, и определить, может ли быть полезен лучший дизайн класса.

Например, предположим, что некоторые типы объектов хотят получать уведомления, если происходит "Wowzo"; другие типы не заботятся. Кроме того, ожидается, что будут списки, содержащие объекты обоих типов, и потребуется уведомить, уведомить все элементы, которые заботятся. Можно использовать вышеупомянутый шаблон проверки типов для уведомления только тех элементов, которые в этом нуждаются, но может быть более эффективный подход: можно определить два интерфейса: IAcceptWowzoNotifications а также IActOnWowzoNotificationsпричем последний наследует первый. Ожидается, что классы, которые реализуют первое, но не второе, будут реализовывать уведомления Wowzo с пустыми методами. Если каждый элемент в списке реализует IAcceptWowzoNotifications, независимо от того, выполняет ли он что-либо с таким уведомлением, будет проще просто уведомить всех в списке, чем проверять, какие элементы требуют уведомления.

Обратите внимание, что этот подход не совсем без затрат. Во-первых, потому что нет интерфейсов, предлагающих реализации методов по умолчанию, каждый тип, который реализует IAcceptWowzoNotifications нужно будет определить методы заглушки. Кроме того, если единственная причина, по которой элемент будет помещен в список, состоит в том, чтобы отправлять ему уведомления Wowzo, и если уведомление всех в списке будет более частым явлением, чем добавление элементов в список, было бы лучше проверить, предметы реализует IActOnWowzoNotifications перед добавлением их в список, чем слепо добавлять элементы в список, которые могут или не должны быть там.

Иерархии наследования интерфейсов очень мощные и во многих случаях используются недостаточно. Тот факт, что реализация интерфейса не должна беспокоиться о том, какие члены пришли из каких интерфейсов-предков, облегчает разделение интерфейсов с гораздо меньшей головной болью, чем при разделении классов.

Другие вопросы по тегам