Союз типов C # с интерфейсами - PullRequest
3 голосов
/ 06 июля 2010

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

Для пояснения, скажем, у меня есть интерфейс с именем IGraph , где T - данные, которые содержит каждый узел. Теперь я также хочу иметь интерфейсы для IUndirectedGraph , IDigraph и IWeightedGraph , где E - это тип, используемый в качестве веса.

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

Итак, допустим, у меня есть два объявления классов:

class WeightedAdjacencyListGraph<T,E> : IUndirectedGraph<T>, IWeightedGraph<T,E>

class WeightedAdjacencyMatrixGraph<T,E> : IUndirectedGraph<T>, IWeightedGraph<T,E>

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

<IUndirectedGraph<object>+IWeightedGraph<object,double>> MyGraph = new WeightedAdjacencyListGraph<object,double>();
MyGraph = new WeightedAdjacencyMatrixGraph<object,double>();

Очевидно, что объявление типа переменной не является правильным синтаксисом C #, но что бы я здесь поместил? Нужно ли создавать новый интерфейс для каждой комбинации интерфейсов? Является ли мой дизайн в корне ошибочным, и если да, что я должен сделать, чтобы исправить его?

Редактировать: я решил создать разные пространства имен для ориентированных / ненаправленных графов и хранить общие интерфейсы (такие как IWeightedGraph ) в корневом пространстве имен. Затем я в основном создаю комбинированные интерфейсы, о которых я упоминал выше (которые также были отмечены в ответах). Я полагаю, что ориентированные / ненаправленные графы вряд ли будут иметь что-то общее, когда дело доходит до забавных алгоритмов.

Ответы [ 4 ]

4 голосов
/ 06 июля 2010

Если вы хотите оговорить, что оба контракта выполнены, чтобы использовать тип в определенной ситуации, тогда объявите новый интерфейс, который требует обоих, и реализуйте это:

public interface IUndirectedAndWeightedGraph<T,E> :
    IUndirectedGraph<T>, IWeightedGraph<T,E>
{
}

Любой класс, который реализует это, также выполняет отдельные контракты, так что вы все равно можете рассматривать любой класс, который реализует IUndirectedAndWeighted, как просто IUndirected и т. Д.

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

2 голосов
/ 06 июля 2010

Вы можете сделать это в ограниченном случае параметров метода с помощью обобщений и ограничений типов:

void ProcessGraph<TGraph>(TGraph graph)
    where TGraph: IUndirectedGraph<T>, IWeightedGraph<T,E>
{
}

Подвох в том, что не очень хорошо играет с перегрузкой метода.

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

1 голос
/ 06 июля 2010

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

IComboGraph<T, E> : IUndirectedGraph<T>, IWeightedGraph<T,E>

class WeightedAdjacencyListGraph<T,E> : IComboGraph<T, E>

class WeightedAdjacencyMatrixGraph<T,E> : IComboGraph<T, E>

Затем используйте его так:

IComboGraph<object, double> MyGraph = new WeightedAdjacencyListGraph<object,double>();
MyGraph = new WeightedAdjacencyMatrixGraph<object,double>();

РЕДАКТИРОВАТЬ: я должен добавить, что ваш комбинированный интерфейс не должен иметь ничего, но быть определеннымнаследовать от этих интерфейсов.

0 голосов
/ 06 июля 2010

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

  1. Создание интерфейсов для всех комбинаций является возможным решением, но также кошмаром, если у вас есть три, четыре или более интерфейсов.

  2. В C # 4.0 вы можете использовать dynamic. Это стоит немного строгой статической типизации и, возможно, некоторой производительности - я бы тоже попытался этого избежать.

  3. Где возможно, вы можете просто использовать конкретные типы или var, чтобы сделать изменения проще за счет более плотного сцепления.

  4. Вы также можете написать обертку, которая реализует все интерфейсы и самостоятельно отправлять вызовы в упакованный экземпляр - тоже неприятно.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...