【问题标题】:must implement EITHER interface A OR interface B (exactly one)必须实现接口 A 或接口 B(正好一个)
【发布时间】:2013-03-11 17:09:38
【问题描述】:

我的界面如下所示:

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

我将如何布置接口(也许我必须有一些抽象类)以支持这种“一种或另一种”设计?

【问题讨论】:

标签: c# oop interface


【解决方案1】:

你不能。您要么显式实现接口,要么不实现。您所描述的实际上是“将存在方法 A 方法 B”,但这不是 C#(或我知道的任何其他语言!)中存在的概念。 p>

如果该类不实现其他两个接口之一,您将能够得到的最接近的方法是在使用您的接口的代码中抛出异常。

或者,我想你可以有一个基类,如果它没有实现一个或其他接口,它的构造函数会抛出异常。这会给你一个更早的检查,但它仍然是一个运行时检查,我个人认为这是一个可怕的想法。

【讨论】:

  • +1 用于确认我已经知道的,但仍然不是一个很好的解决方案(正如你自己所说的)
【解决方案2】:

我相信你过度限制了真正的问题。这与我在游戏引擎中遇到的问题非常相似,其中六角网格上的坐标可以位于规范参考框架(120 度的轴,方便大多数内部游戏功能)或矩形(用户)参考框架轴成 90 度(方便大多数用户可见的游戏功能)。

我通过构建一个单独的类 Coords 来解决这个问题,该类明确地实现了接口 ICoordsCanon 和 ICoordsUser。实际坐标被延迟存储并通过如下自动转换进行评估:

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() 用于根据需要在两者之间进行转换。

根据大众需求,这里是接口定义和实现。

  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

【讨论】:

  • 我喜欢你在这里的方向 - 你能粘贴你的界面定义吗?
  • 我不认为这对我有用,因为我想保持定义分开(它们可能是可转换的,但它们不需要是)。另外,我希望实现者提供 ILocation 的实现,而不是我。
  • 这听起来像是我现在正在进行的重构。这最初是专门为 hexGrids 实现的。我现在只将基类功能抽象到抽象类 Coords 中,在子类 HexCoords 中使用 Hex-grid 特定代码。然后将构建和测试一个新的实现 IsometricCoords。重构进展顺利,但至少要过几天才会上传。
【解决方案3】:

我不知道有任何方法可以在编译时强制执行此操作。我认为你必须通过使用一个基类来进行运行时检查,如果两个接口都实现了,则该基类会引发异常。

但是,这不会阻止某人绕过您的基类并自己实现接口,我不知道有什么办法可以阻止。

【讨论】:

    【解决方案4】:

    您可以尝试代码合同。后置条件。像这样的东西

    [ContractClassFor(typeof(IOpportunity))]
    public abstract class OpportunityContract : IOpportunity
    {
    
        public ILocation Location
        {
            get { Contract.Ensures(Contract.Result<ILocation>() is IHierarchicalEntityWithParentNames || Contract.Result<ILocation>() is IHierarchicalEntityWithParentIds); }
        }
    }
    

    【讨论】:

    • 我认为这不会提供任何编译时检查 - 是吗?
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2013-04-14
    • 2019-07-26
    • 1970-01-01
    • 2018-08-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多