2

次のようなインターフェイスがあります。

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 オブジェクトにもこれらのインターフェイスの 1 つを実装する必要があります。

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メソッドを持つ 1 つのインターフェイスに結合することはできません。

これらのインターフェイスを使用するコードには、2 つのクラスがありますIHierarchicalEntityWithParentNamesIHierarchicalEntityWithParentIds

この「どちらか一方」の設計をサポートするには、インターフェイスをどのようにレイアウトしますか (おそらく、いくつかの抽象クラスが必要です)。

4

4 に答える 4

2

あなたは本当の問題を過度に制約していると思います。これは、私がゲーム エンジンで遭遇した問題と非常によく似ています。16 進グリッド上の座標は、標準参照フレーム (ほとんどの内部ゲーム機能に便利な 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;

Coords のコンストラクターはプライベートであり、パブリックな静的関数 NewUserCoords(...) および NewCanonCoords(...) が定義されています。

実装は実際には ... でも ... でもありませんが、アプリケーションにはそのように実装されているように見えます。ほとんどのアプリケーションの使用法は、ICoordsCanon オブジェクトまたは ICoordsUser オブジェクトのいずれかで機能します。ICoordsCanon.User() と ICoordsUser.Canon() の 2 つのメソッドは、必要に応じて 2 つの間で変換するために存在します。

一般的な要望により、インターフェースの定義と実装を以下に示します。

  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

于 2013-03-11T17:29:03.463 に答える
2

できません。インターフェイスを明示的に実装するか、実装しないかのどちらかです。あなたが説明しているのは、事実上「メソッド Aまたはメソッド B のいずれかが存在する」ということですが、それは C# (または私が知っている他の言語!) に存在する概念ではありません。

最も近い方法は、クラスが他の 2 つのインターフェイスのいずれかを実装していない場合に、インターフェイスを使用するコードで例外をスローすることです。

または、いずれかのインターフェイスも実装していない場合、コンストラクターが例外をスローする基本クラスを作成できると思います。これにより、以前のチェックが可能になりますが、それでもランタイム チェックであり、個人的には恐ろしい考えだと思います。

于 2013-03-11T17:13:35.360 に答える
0

コードコントラクトを試すことができます。事後条件。このようなSmth

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

    public ILocation Location
    {
        get { Contract.Ensures(Contract.Result<ILocation>() is IHierarchicalEntityWithParentNames || Contract.Result<ILocation>() is IHierarchicalEntityWithParentIds); }
    }
}
于 2013-03-11T17:58:37.283 に答える
0

コンパイル時にこれを強制する方法を知りません。両方のインターフェースが実装されている場合、例外をスローする基本クラスを使用して、これを実行時チェックにする必要があると思います。

ただし、誰かがあなたの基本クラスをバイパスしてインターフェイス自体を実装するのを止めることはできません。それを防ぐ方法はわかりません。

于 2013-03-11T17:13:08.607 に答える