2013-03-11 142 views
2

我有一個看起來像這樣的接口:必須實現接口A或接口B(只有一個)

public interface IOpportunity 
{ 
    string Name { get; } 
    string Description { get; } 
    ILocation Location { get; } 
} 

public interface ILocation : IHierarchicalEntity 
{ 
    int OpptyCount { get; } 
} 

public interface IHierarchicalEntity 
{ 
    string SID { get; } 
    string Name { get; } 
} 

不過,我想ILocation對象,也能實現這些接口中的一個:

public interface IHierarchicalEntityWithParentNames : IHierarchicalEntity 
{ 
    /// <summary> 
    /// Returns the lowest level that this heirarchy goes (0 for a flat hierarchy, 1 for a two-level etc.) 
    /// </summary> 
    int LeafLevel { get; } 

    /// <summary> 
    /// Returns the name of the Segment for the given level (0 for a root node, n for leaf node, where n = LeafLevel) 
    /// </summary> 
    /// <param name="level"></param> 
    /// <returns></returns> 
    string GetNameForLevel(int level); 
} 


public interface IHierarchicalEntityWithParentIds : IHierarchicalEntity 
{ 
    IHierarchicalEntityWithParentIds ParentEntity { get; } 
    string ParentSID { get; } 
} 

由於我寫的代碼的性質,我不能將這些接口組合成具有某種GetParent方法

在代碼一個接口,消耗這些接口,我有兩個類 - 一個消耗ILocation對象如果它是IHierarchicalEntityWithParentNames和另一個如果它是IHierarchicalEntityWithParentIds

我將如何佈局接口(也許我必須有一些抽象類)來支持有這個「一個或另一個」的設計?

+0

我編輯了你的標題。請參閱:「[應該在其標題中包含」標籤「](http://meta.stackexchange.com/questions/19190/)」,其中的共識是「不,他們不應該」。 – 2013-03-11 17:12:44

回答

0

我不知道有什麼方法可以在編譯時執行此操作。我認爲你將不得不通過使用基類來做這個運行時檢查,如果兩個接口都被實現的話,這個基類會拋出一個異常。

但是,這不會阻止某人繞過您的基類並自己實現接口,並且我知道無法阻止它。

1

你不行。你明確地實現一個接口,或者你不明白。你所描述的實際上是「方法A 方法B將存在」,但這不是存在於C#中的概念(或者我知道的任何其他語言!)。

如果這個類沒有實現其他兩個接口之一,那麼您將能夠得到的最接近的結果就是在消耗接口的代碼中拋出一個異常。

或者,我想你可以有一個基類,如果它的構造函數沒有實現一個或另一個接口,它的構造函數將拋出一個異常。這將給你一個更早的檢查,但它仍然是一個運行時檢查,並且,我個人認爲這是一個可怕的想法。

+0

+1用於確認我已經知道的內容,但仍然不是一個好的解決方案(如你自己所說的) – 2013-03-11 17:33:47

2

我相信你是過度約束真正的問題。這與我在遊戲引擎中遇到的問題非常相似,其中六角形網格上的座標可以是規範參考框架(120度的軸,對於大多數內部遊戲功能很方便),也可以是矩形(用戶)參考框架軸角度爲90度(便於大多數用戶可見的遊戲功能)。

我通過構建一個明確實現ICoordsCanon和ICoordsUser接口的Coords類來解決這個問題。實際的座標存儲懶洋洋地評價和使用像這樣的自動轉換:

protected static IntMatrix2D MatrixUserToCanon; 
protected IntVector2D VectorCanon { 
    get { return ! isCanonNull ? _vectorCanon 
          : VectorUser * MatrixUserToCanon; } 
    set { _vectorCanon = value; isUserNull = true; } 
} IntVector2D _vectorCanon; 
bool isCanonNull; 

protected static IntMatrix2D MatrixCanonToUser; 
protected IntVector2D VectorUser { 
    get { return ! isUserNull ? _vectorUser 
          : VectorCanon * MatrixCanonToUser; } 
    set { _vectorUser = value; isCanonNull = true; } 
} IntVector2D _vectorUser; 
bool isUserNull; 

的座標中的構造函數是私有的,與公共定義靜態函數NewUserCoords(...)和NewCanonCoords(...)。

雖然實現並不是真的......或者......,但它似乎是應用程序實現的。大多數應用程序用途可以與ICoordsCanon對象或ICoordsUser對象一起使用; ICoordsCanon.User()和ICoordsUser.Canon()存在兩種方法用於根據需要在兩者之間進行轉換。

根據大衆的需求,下面是接口定義和實現。

public interface ICoordsUser { 
    int    X   { get; } 
    int    Y   { get; } 
    IntVector2D  Vector { get; set; } 
    ICoordsCanon Canon  { get; } 
    //ICoordsUser  Clone(); 
    string   ToString(); 
    int    Range(ICoordsUser coords); 
    IEnumerable<NeighbourCoords> GetNeighbours(Hexside hexsides); 
    } 

    public partial class Coords { 
    int   ICoordsUser.X   { get { return VectorUser.X; } } 
    int   ICoordsUser.Y   { get { return VectorUser.Y; } } 
    IntVector2D ICoordsUser.Vector  { get { return VectorUser; } 
              set { VectorUser=value; } } 
    ICoordsCanon ICoordsUser.Canon  { get { return this;   } } 
    //ICoordsUser ICoordsUser.Clone() { return NewUserCoords(VectorUser); } 
    string  ICoordsUser.ToString() { return VectorUser.ToString(); } 

    IEnumerable<NeighbourCoords> ICoordsUser.GetNeighbours(Hexside hexsides) { 
     return GetNeighbours(hexsides); 
    } 
    int ICoordsUser.Range(ICoordsUser coords) { return Range(coords.Canon); } 
    } 
} 

public interface ICoordsCanon { 
    int    X   { get; } 
    int    Y   { get; } 
    IntVector2D  Vector { get; set; } 
    ICoordsCustom Custom { get; } 
    ICoordsUser  User  { get; } 
    //ICoordsCanon Clone(); 
    string   ToString(); 
    int    Range(ICoordsCanon coords); 
    IEnumerable<NeighbourCoords> GetNeighbours(Hexside hexsides); 
    } 
    public partial class Coords { 
    int    ICoordsCanon.X   { get { return VectorCanon.X; } } 
    int    ICoordsCanon.Y   { get { return VectorCanon.Y; } } 
    IntVector2D  ICoordsCanon.Vector  { get { return VectorCanon; } 
               set { VectorCanon=value; } } 
    ICoordsUser  ICoordsCanon.User  { get { return this; } } 
    ICoordsCustom ICoordsCanon.Custom  { get { return this; } } 
    //ICoordsCanon ICoordsCanon.Clone() { return NewCanonCoords(this.VectorCanon); } 
    string   ICoordsCanon.ToString() { return VectorCanon.ToString(); } 

    IEnumerable<NeighbourCoords> ICoordsCanon.GetNeighbours(Hexside hexsides) { 
     return GetNeighbours(hexsides); 
    } 
    int ICoordsCanon.Range(ICoordsCanon coords) { return Range(coords); } 
    } 

請注意,我並沒有包括類COORDS的整個定義,因爲這僅僅是過於龐大帖子。整個實施可在CodePlex上獲得:HexGrid Utilities

+0

我喜歡你要去的方向 - 你可以粘貼你的界面定義嗎? – 2013-03-11 17:37:54

+0

我不認爲這對我有用,因爲我想保持定義不同(它們可能是可轉換的,但它們不需要)。另外,我希望實現者爲ILocation提供實現,而不是我。 – 2013-03-11 17:59:38

+0

這聽起來像是我現在正在進行的重構。這最初是專門爲hexGrids實現的。我現在只將基類功能抽象到抽象類Coords中,在子類HexCoords中使用Hex-grid特定的代碼。然後將建立並測試新的實施IsometricCoords。重構進展順利,但至少幾天不會上傳。 – 2013-03-11 18:10:57

0

您可以嘗試代碼合同。後置條件。像這樣的笑話

[ContractClassFor(typeof(IOpportunity))] 
public abstract class OpportunityContract : IOpportunity 
{ 

    public ILocation Location 
    { 
     get { Contract.Ensures(Contract.Result<ILocation>() is IHierarchicalEntityWithParentNames || Contract.Result<ILocation>() is IHierarchicalEntityWithParentIds); } 
    } 
} 
+0

我不認爲這提供了任何編譯時檢查 - 是嗎? – 2013-03-11 19:18:06