Обобщения с иерархией ограничений - PullRequest
1 голос
/ 22 мая 2010

Я сейчас сталкиваюсь с очень тревожной проблемой:

interface IStateSpace<Position, Value>
where Position : IPosition           // <-- Problem starts here
where Value : IValue                 // <-- and here as I don't
{                                    //     know how to get away this
                                     //     circular dependency!
                                     //     Notice how I should be
                                     //     defining generics parameters
                                     //     here but I can't!
    Value GetStateAt(Position position);
    void SetStateAt(Position position, State state);
}

Как вы здесь увидите, и IPosition, IValue и IState зависят друг от друга. Как мне это сойдет с рук? Я не могу придумать какой-либо другой дизайн, который обойдет эту круговую зависимость и все же точно описывает, что я хочу сделать!

interface IState<StateSpace, Value>
where StateSpace : IStateSpace        //problem
where Value : IValue                  //problem
{
    StateSpace StateSpace { get; };
    Value Value { get; set; }
}

interface IPosition
{
}

interface IValue<State>
where State : IState {      //here we have the problem again
    State State { get; }
}

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

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

Спасибо

Ответы [ 2 ]

5 голосов
/ 22 мая 2010

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

public interface IStateSpace
{
    IState GetStateAt(IPosition position);
    void SetStateAt(IPosition position, IState state);
}

public interface IState
{
    IStateSpace StateSpace { get; }
    IValue Value { get; set; }
}

public interface IPosition
{
}

public interface IValue
{
    IState State { get; }
}

Затем вы можете создавать конкретные реализации.

internal class MatrixStateSpace : IStateSpace
{
    IState GetStateAt(IPosition position)
    {
        return this.Matrix[position];
    }

    void SetStateAt(IPosition position, IState state)
    {
        this.Matrix[position] = state;
    }
}

internal class GraphStateSpace : IStateSpace
{
    IState GetStateAt(IPosition position)
    {
        return this.Graph.Find(position).State;
    }

    void SetStateAt(IPosition position, IState state)
    {
        this.Graph.Find(position).State = state;
    }
}

И теперь вы можете решить использовать MatrixStateSpace или GraphStateSpace экземпляров, когда требуется экземпляр IStateSpace. То же самое верно и для всех других типов.

Сложной частью реализации является необходимость создания новых экземпляров. Например, может быть метод IState AddNewState(), определенный в IStateSpace. Любая конкретная реализация IStateSpace сталкивается с проблемой создания экземпляра IState (в идеале) без знания какого-либо конкретного типа, реализующего IState. Это фабрики, внедрение зависимостей и связанные с ними концепции.

3 голосов
/ 22 мая 2010

Не совсем понятно, в чем проблема - да, у вас есть циклические зависимости в ваших универсальных типах, но это работает.

У меня есть похожая «проблема» в Буферы протокола : у меня есть «сообщения» и «составители», и они идут парами. Итак, интерфейсы выглядят так:

public interface IMessage<TMessage, TBuilder>
    where TMessage : IMessage<TMessage, TBuilder> 
    where TBuilder : IBuilder<TMessage, TBuilder>

и

public interface IBuilder<TMessage, TBuilder> : IBuilder
    where TMessage : IMessage<TMessage, TBuilder> 
    where TBuilder : IBuilder<TMessage, TBuilder>

Это, конечно, некрасиво, но работает. Что вы хотите, чтобы иметь возможность выразить то, что вы не можете выразить в настоящее время? Некоторые мои мысли об этом вы можете увидеть в моем блоге . (Части 2 и 3 серии о буферах протоколов здесь наиболее актуальны.)

(Кроме того, это сделало бы ваш код более обычным, если бы вы добавили префикс T к параметрам вашего типа. В настоящее время похоже, что State и Value являются просто классами.)

...