如何在C#中声明未指定的方法参数

时间:2016-04-13 14:36:26

标签: c# class

我有这个基本界面,它描述了一个牌手(人和AI)的行为:

interface ICardPlayer<T>
    where T: Carta, new()
{
    // some methods here
    T Pop(UNSPECIFIED ARGUMENTS);
}

Pop功能允许CardPlayer从他的套牌中丢弃一张牌,但在这个级别我不知道该玩家是人类玩家还是AI玩家。 如果它是人类玩家,则该方法将为T Pop(uint index);,但如果它是AI玩家,则该方法将为T Pop()。在这种情况下,该方法必须没有参数,因为AI播放器上的Pop功能会调用AI的方法来丢弃正确的卡。 所以我也会有这两个接口:

interface IHumanCardPlayer<T> : ICardPlayer<T>
    where T: Carta, new()
{
    // some methods here
    T Pop(uint index);
}

interface IAICardPlayer<T>
    where T: Carta, new()
{
    // some methods here
    T Pop();
}

我不必拥有所有两种方法:如果玩家是人类玩家,他必须调用Pop方法,为其提供他将丢弃的卡的索引,并且他不能不带参数调用方法。 同样如果它是一个AI玩家:他必须调用Pop方法而不给它任何参数,并且他不能调用方法Pop(index)

那么,有没有办法在Pop(UNSPECIFIED ARGUMENTS)接口中编写ICardPlayer<T>或者我是否必须编写2个不同的Pop方法而不使用继承?

2 个答案:

答案 0 :(得分:3)

这会破坏接口的目的,因为调用该方法是不可能的(任何可能的调用可能都有特定子类的错误参数)。

你做不到。

答案 1 :(得分:3)

首先,您不使用继承。这不一定是好事或坏事。

其次,你不能,这是一件好事。

接口代表某种公共接口,它公开对一组通用功能的访问。通常,接口的任何实现都应该同样有效 - 它们应该是可互换的,并且使用这些接口的代码不应该关心您给出的具体实现。显然,在您的情况下情况并非如此 - 您希望根据您正在采用的特定实现来调用具有不同参数的接口。这与首先​​使用接口(和继承)的整个想法背道而驰。

然而,你只是不必要地把自己画在角落里。你可以说,界面太大了,你需要为同一个方法提供两组独立的参数。

相反,将人类行为和AI行为分开到不同的层面。 ICardPlayer始终获取int参数。唯一的区别在于如何在不同的地方产生论证 - 在人类玩家的情况下,它是UI的产品,要求他选择卡片。对于AI播放器,它是由某种算法产生的。

因此,您将拥有一个代表行动的界面&#34;选择一张卡&#34;:

interface IPlayer
{
  int PickCardToDiscard();
}

你离开了如何实施:

public class HumanPlayer: IPlayer
{
  private readonly IGui gui;

  public HumanPlayer(IGui gui)
  {
    this.gui = gui;
  }

  public int PickCardToDiscard()
  {
    return gui.AskForCardSelection("Pick a card to discard.");
  }
}

public class StupidPlayer: IPlayer
{
  public int PickCardToDiscard()
  {
    return 42; // Feeling lucky
  }
}

现在您的界面是一致的,并且您已将特定实施移动到它们所属的位置。当实例化 ICardPlayer时,您总是知道您是否需要人类玩家或AI玩家。但那只是你所关心的唯一的地方。抽象的力量 - 精心设计的界面允许您将自己与具体事物隔离开来并专注于抽象(这是一个小得多的问题空间)。当游戏引擎想要选择一张卡时,它只需要调用

var cardToDiscard = deck.Pop(player.PickCardToDiscard());

它并不关心玩家是人还是人工智能,并且它为你提供了在其他实现中连线的机会 - 比如不同的人工智能策略,或者人类在网络上玩。

请记住,每一段代码都能更好地为自己付出代价 - 如果它没有益处,它就会积极地堕落。一般来说抽象也是如此 - 如果抽象不支付租金,修复或丢失它。在你的情况下,抽象显然是愚蠢的 - 即使你明确设计它的前两个案例它也不起作用。如果您有像#34;使用接口和#34;编写代码这样的任务,那么您可能会做的事情,并且您不知道如何设计实际为您的代码增加价值的接口。接口方面没有任何意义,或者为了抽象而抽象。 Mkae代码支付租金。

最后,有可选参数的情况很有意义。但关键是这些参数必须仍然是合同的一部分,并且所有实现必须同样有效。例如,您可能有这样的日志记录界面:

interface ILogger
{
  void Log(string message, int? severity);
}

你可以指定严重性,或者你可以使用null - 但选择并不取决于ILogger的具体实现,它只取决于调用者 - 有时候,他想要指定严重性,有时他不会。