2010-05-22 78 views
1

我目前面臨的一個非常令人不安的問題:泛型與約束的層次

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將作爲矩陣作爲圖實現,等等)。

我能脫身嗎?你通常如何解決這類問題?在這些情況下使用哪種設計?

感謝

回答

3

這並不完全清楚問題是什麼 - 是的,你的泛型類型中有循環依賴關係,但這是有效的。

我在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的一些想法。 (關於協議緩衝區的系列的第2部分和第3部分在這裏是最相關的。)

(順便說一句,它會使你的代碼更傳統的,如果你想補充T前綴爲您的類型參數。目前,它看起來像StateValue只是類。)

5

我看不到你想達到什麼 - 爲什麼要使用泛型強制的具體類型爲你的接口?這似乎完全違背接口的意圖 - 不使用具體類型。以下非泛型定義有什麼問題?

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實例(理想情況下)的問題。這是工廠,依賴注入以及相關概念的發揮。