我有一个如下所示的界面:
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 :(得分:2)
我相信你过分约束了真正的问题。这与我在游戏引擎中遇到的问题非常相似,其中十六进制网格上的坐标可以在规范参考系中(120度的轴,方便大多数内部游戏功能)或矩形(用户)参考系中轴为90度(方便大多数用户可见的游戏功能)。
我通过buidlng一个类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
答案 1 :(得分:1)
你做不到。您可以显式实现接口,也可以不实现。您所描述的实际上是“方法A 或方法B将存在”,但这不是C#中存在的概念(或我所知道的任何其他语言!)。
如果类没有实现其他两个接口中的一个,那么你能够获得的最接近的是在代码中抛出一个异常消耗你的接口。
或者,我想你可以有一个基类,如果它没有实现一个或另一个接口,它的构造函数将抛出异常。这会给你一个较早的检查,但它仍然是一个运行时检查,并且,就个人而言,我认为这是一个可怕的想法。
答案 2 :(得分:0)
我不知道有任何方法可以在编译时强制执行此操作。我认为你必须通过使用一个基类来进行运行时检查,如果两个接口都被实现则抛出异常。
但是,这不会阻止某人绕过你的基类并自己实现接口,我知道没办法阻止它。
答案 3 :(得分: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); }
}
}