【问题标题】:Generics with constraints hierarchy具有约束层次结构的泛型
【发布时间】:2010-05-22 01:08:25
【问题描述】:

我目前面临一个非常令人不安的问题:

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);
}

正如您将在这里看到的,IPositionIValueIState 相互依赖。我该怎么办?我想不出任何其他设计可以绕过这种循环依赖,并且仍然准确地描述了我想要做的事情!

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 将被实现为矩阵和图等)。

我能逃脱惩罚吗?您一般如何解决此类问题?在这些情况下使用哪种设计?

谢谢

【问题讨论】:

    标签: c# generics oop circular-dependency


    【解决方案1】:

    我看不到您想要实现的目标 - 为什么要通过使用泛型将具体类型强制到您的接口中?这似乎完全违背了接口的意图——不使用具体类型。以下非泛型定义有什么问题?

    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;
        }
    }
    

    现在您可以决定在需要 IStateSpace 实例的情况下使用 MatrixStateSpaceGraphStateSpace 实例。当然,所有其他类型也是如此。

    实现中具有挑战性的部分是您必须创建新实例。例如,IStateSpace 中可能定义了一个方法IState AddNewState()IStateSpace 的任何具体实现都面临着在不知道任何实现IState 的具体类型的情况下创建IState 实例(理想情况下)的问题。这就是工厂、依赖注入和相关概念发挥作用的原因。

    【讨论】:

      【解决方案2】:

      问题出在哪里并不完全清楚 - 是的,您的泛型类型中存在循环依赖关系,但这是可行的。

      我在Protocol Buffers 中有一个类似的“问题”:我有“消息”和“构建器”,它们是成对出现的。所以接口看起来像这样:

      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>
      

      这当然很难看,但它确实有效。你想表达什么是你目前无法表达的?您可以看到我对此on my blog 的一些想法。 (关于 Protocol Buffers 系列的第 2 部分和第 3 部分在这里最为相关。)

      (顺便说一句,如果您将T 前缀添加到您的类型参数,它会使您的代码更传统。目前看起来StateValue 只是类。)

      【讨论】:

        猜你喜欢
        • 2023-04-03
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2016-09-14
        相关资源
        最近更新 更多