【问题标题】:How to properly set type of inherited property如何正确设置继承属性的类型
【发布时间】:2017-05-26 11:12:44
【问题描述】:

我有抽象基类。

public abstract class TurnBasedGameReferee
{
    public ITurnBasedGamePlayer CurrentPlayer { get; private set; }
    public TurnBasedGameField PlayingField { get; protected set; }
    /*snipped*/
}

我有继承类:

public class TicTacToeReferee : TurnBasedGameReferee { /*snipped*/ }

在 TicTacToeReferee 中,我应该指的是这样的属性:

(ITicTacToePlayer)this.CurrentPlayer;
(TicTacToeGameField)this.PlayingField;

我认为,这里每次都使用显式转换是不正确的。

有没有办法在TicTacToeReferee 的上下文中这样做,这些CurrentPlayerPlayingFieldITicTacToePlayerTicTacToeGameField 类型而无需每次都使用显式转换?

【问题讨论】:

  • 取决于你想在演员阵容后对结果做什么。无论如何,转换到接口通常是一个好主意,不知道是什么让你认为它不是。展示这些类的代码,以及你在演员之后对它们做什么。
  • 您可以在裁判上使用通用参数来指定球员和场地的类型。尽管我可能会建议您避免继承,除非您有特定的用例。
  • 无论如何,如果您想访问派生类的成员,您使用对该类的强制转换。

标签: c# oop inheritance types type-conversion


【解决方案1】:

您可以使用泛型。

public abstract class TurnBasedGameReferee<TPlayer, TField>
                  where TPlayer : ITurnBasedGamePlayer 
                  where TField : TurnBasedGameField 
{
    public TPlayer CurrentPlayer { get; private set; }
    public TField PlayingField { get; protected set; }
    /*snipped*/
}

public class TicTacToeReferee : TurnBasedGameReferee<ITicTacToePlayer, TicTacToeGameField>

假设 ITicTacToePlayer 继承自 ITurnBasedGamePlayerTicTacToeGameField 继承自 TurnBasedGameField ,但我认为这就是您的意思。

【讨论】:

  • 你的建议是我想要的,但这并不意味着我可以不小心做出例如new TicTacToeReferee&lt;IChessPlayer, ChessField&gt;?如何处理?
  • 你可以,如果 IChessPlayer 和 ChessField 实现了 TurnBased 类。不好吗?国际象棋是一个回合制游戏,您可以创建新接口 ITurnBasedNotChess 并让您的井字游戏玩家实现它,而国际象棋玩家则不需要,您可以在泛型类声明中使用此接口。
  • 但是 TicTacToeReferee 不能用 ChessField 和 ChessPlayers 组织比赛,这就是为什么我认为它不好。 TicTacToeReferee 必须仅与 TicTacToeFieldITicTacToePlayer 一起使用
  • @KgOfHedgehogs 您的解决方案在执行方面并没有做得更好。
【解决方案2】:

基于Maksim Simkin's 答案和您的 cmets,您可以强制执行所需的类型安全,但代价是额外的泛型类型参数并增加了解决方案的复杂性:

public interface ITurnBasedGame { }
public interface ITurnBasedGamePlayer<TGame> where TGame : ITurnBasedGame { }
public abstract class TurnBasedGameField<TGame> where TGame : ITurnBasedGame { }

public abstract class TurnBasedGameReferee<TGame, TPlayer, TField>
    where TGame: ITurnBasedGame
    where TPlayer: ITurnBasedGamePlayer<TGame>
    where TField: TurnBasedGameField<TGame>
{
    public TPlayer CurrentPlayer { get; private set; }
    public TField PlayingField { get; protected set; }
}

现在,考虑以下类型:

public class TicTacToeGame : ITurnBasedGame { }
public class TicTacToePlayer : ITurnBasedGamePlayer<TicTacToeGame> { }
public class TicTacToeGameField : TurnBasedGameField<TicTacToeGame> { }
public class ChessGame : ITurnBasedGame { }
public class ChessPlayer : ITurnBasedGamePlayer<ChessGame> { }
public class ChessGameField : TurnBasedGameField<ChessGame> { }

以下将是编译时错误:

public class TicTacToeReferee: TurnBasedGameReferee<TicTacToeGame, ChessPlayer, ChessGameField> { }

说了这么多,这当然是不值得的,只是为了避免将接口转换为类的具体类型。如果你不喜欢所有的演员表,那么你总是可以实现私有的帮助属性来清理你的代码:

public class TicTacToeReferee : TurnBasedGameReferee
{
     private TicTacToePlayer CurrentTicTacToePlayer => CurrentPlayer as TicTacToePlayer;
     private TicTacToeGameField TicTacToePlayingField => PlayingField as TicTacToeGameField;
     ....
}

并且只需在您的内部实现中使用这些属性。

【讨论】:

  • 我认为这正是我在提问时所需要的。但现在我在私人助手和显式转换之间进行选择,因为毕竟答案,我不认为显式转换是什么坏事
  • 我创建了this helpers。在这里可以使用newbase 吗?
  • @KgOfHedgehogs 不,这实际上非常可怕,因为现在你的混凝土 TicTacTooReferee 违反了 TurnBasedGameReferee 的合同;您正在隐藏应该公开的基类成员。使用您的代码,您班级之外的任何人都无法调用ticTacToeReferee.CurrentPlayer,并且需要将其转换为基类才能这样做。
  • 谢谢。现在我明白了
  • 抱歉,问题迟到了,但如果我将其公开怎么办?它们可以在类外调用,但它们的类型不是来自抽象基,而是类似的井字游戏继承类型
猜你喜欢
  • 2019-03-09
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-10-06
  • 1970-01-01
  • 2022-11-02
  • 1970-01-01
相关资源
最近更新 更多