10

这与 C# 中使用的约定有关。

我有一个有两个参数(X 和 Y 坐标)的方法。这些坐标表示“图块”可能驻留的位置。如果瓦片位于这些坐标处,则该方法返回其编号。如果这些坐标处没有瓷砖,我想知道该方法应该如何表现。

我看到三个选项:

  1. 使用例外。每次 Method 没有找到 tile 时,我可能会引发异常。但是,由于这种情况并不罕见,因此此选项是最糟糕的选项。
  2. 用老式的 C++ 方式执行,如果没有 tile,则返回 -1。
  3. 将瓷砖编号作为参考参数并将方法的返回类型更改为布尔值以显示是否有瓷砖。但这对我来说似乎有点复杂。

所以我该怎么做?

4

12 回答 12

23

您可以返回 null,并在调用代码上检查这一点。

当然,您必须使用可为空的类型:

int? i = YourMethodHere(x, y);
于 2009-06-05T17:20:40.977 回答
20

返回-1。

这不仅仅是 C++ 约定,它在 .NET Framework 中也很常见 - 例如,String.IndexOf 之类的方法或 SelectedIndex 之类的属性用于表示列表的控件。

编辑

只是为了详细说明,在您的问题中的三个选项(异常,返回 -1,输出参数)中,返回 -1 是要走的路。例外是针对特殊情况,Microsoft 编码指南建议尽可能避免使用 out 参数。

在我看来,返回 -1(假设它总是无效值)、返回可为空的 int 或返回 Tile 对象都是可接受的解决方案,您应该选择与应用程序的其余部分最一致的方案。我无法想象任何开发人员都会在以下任何方面遇到丝毫困难:

int tileNumber = GetTile(x,y);
if  (tileNumber != -1)
{
   ... use tileNumber ...
}


int? result = GetTile(x,y);
if (result.HasValue)
{
    int tileNumber = result.Value; 
   ... use tileNumber ...
}


Tile tile = GetTile(x,y);
if (tile != null)
{
   ... use tile ...
}

我不确定我是否理解 Peter Ruderman 关于使用 int 的评论“比返回可空类型更有效”。我原以为任何差异都可以忽略不计。

于 2009-06-05T17:21:38.853 回答
17

异常是针对特殊情况的,因此在已知预期的错误情况下使用异常是“不好的”。现在,您也更有可能在各处使用 try-catch 来专门处理此错误,因为您希望这种错误情况会发生。

如果您唯一的错误条件(例如 -1)与实际值混淆,则将返回值作为参数是可以接受的。如果你可以有一个负数,那么这是一个更好的方法。

可为空的 int 是引用参数的一种可能替代方法,但是您正在使用它创建对象,因此如果“错误”是例行程序,那么您可能会比引用参数做更多的工作。正如 Roman 在其他地方的评论中指出的那样,您将遇到 C# 与 VB的问题,因为引入可空类型为时已晚,以至于 VB 无法像 C# 那样提供良好的语法糖。

如果您的图块只能是非负数,则返回 -1 是一种可接受的传统方式来指示错误。就性能和内存而言,它也是最便宜的。


其他需要考虑的是自我记录。使用 -1 和例外是惯例:您必须编写文档以确保开发人员了解它们。使用int?返回或引用参数可以更好地自我描述,并且不需要开发人员知道如何处理错误情况的文档。当然 :) 您应该始终编写文档,就像您应该每天使用牙线一样。

于 2009-06-05T17:24:44.297 回答
6

使用可为空的返回值。

int? GetTile(int x, int y) {
   if (...)
      return SomeValue;
   else
      return null;
}

这是最清晰的解决方案。

于 2009-06-05T17:23:47.260 回答
3

如果您的方法可以访问底层 tile 对象,另一种可能性是返回 tile 对象本身,如果没有这样的 tile,则返回 null。

于 2009-06-05T17:23:21.160 回答
2

我会选择选项 2。你是对的,在这种常见情况下抛出异常可能对性能不利,使用 out 参数并返回 true 或 false 很有用,但难以阅读。

另外,想想string.IndexOf()方法。如果没有找到,则返回 -1。我会按照那个例子。

于 2009-06-05T17:25:05.653 回答
2

您可以返回 -1,因为这是一种相当常见的 C# 方法。但是,实际返回单击的磁贴可能会更好,如果没有单击任何磁贴,则返回对单例 NullTile 实例的引用。这样做的好处是您可以为返回的每个值赋予具体含义,而不仅仅是一个数字,除了其数值之外没有任何内在含义。'NullTile' 类型的含义非常具体,几乎不会让其他阅读您的代码的读者怀疑。

于 2009-06-05T17:27:06.040 回答
2

最好的选择是也返回布尔值或返回 null。

例如

bool TryGetTile(int x, int y, out int tile);

或者,

int? GetTile(int x, int y);

有几个原因更喜欢“TryGetValue”模式。一方面,它返回一个布尔值,因此客户端代码非常简单,例如:if (TryGetValue(out someVal)) { /* some code */ }。将此与需要硬编码标记值比较(到 -1、0、null、捕获一组特定异常等)的客户端代码进行比较。“幻数”在这些设计中迅速出现,并且将紧耦合分解为一件苦差事。

当预期有标记值、null 或异常时,检查有关使用哪种机制的文档绝对至关重要。如果文档不存在或无法访问,这是一种常见的情况,那么您必须根据其他证据进行推断,如果您做出错误的选择,您只是将自己设置为空引用异常或其他不良缺陷。然而,TryGetValue() 模式非常接近于仅通过它的名称和方法签名来进行自我记录。

于 2009-06-05T17:44:03.003 回答
1

我对您提出的问题有自己的看法,但如上所述,我已相应投票。

至于你没有问的问题,或者至少作为上述所有答案的扩展:我一定会在整个应用程序中保持对类似情况的解决方案。换句话说,无论您选择什么答案,都应在应用程序中保持不变。

于 2009-06-05T17:48:47.740 回答
0

如果方法是低级库的一部分,那么您的标准 .NET 设计可能要求您从方法中抛出异常。

这就是 .NET 框架的一般工作方式。您的更高级别的调用者应该捕获您的异常。

但是,由于您似乎是从 UI 线程执行此操作的,这会影响性能,因为您正在响应 UI 事件 - 我执行 Jay Riggs 已经建议的操作,返回 null,并确保您的调用者检查 null 返回值。

于 2009-06-05T17:23:41.380 回答
0

我将它分为两​​种方法。有类似CheckTileExists(x,y)和的东西GetTile(x,y)。前者返回一个布尔值,指示在给定坐标处是否有图块。第二种方法本质上是您在原始帖子中谈论的方法,除了在给定无效坐标时它应该抛出异常(因为这表明调用者没有首先调用CheckTileExists(),所以它是合法的例外情况。为了速度方面,您可能希望这两种方法共享一个缓存,这样如果它们一个接一个地被调用,则GetTile()功能可以忽略不计。我不知道您是否已经有一个合适的对象来放置这些方法,或者您是否应该在一个新类中将它们作为两个方法。恕我直言,这种方法的性能损失可以忽略不计,代码清晰度的提高远远超过它。

于 2009-06-05T17:41:01.230 回答
0

您是否有可能创建(或可以创建)Tile在坐标处引用的对象?如果是这样,您可以返回对该图块的引用,或者null如果给定坐标处没有图块:

public Tile GetTile(int x, int y) {
    if (!TileExists(x, y)) 
        return null;
    // ... tile lookup here...
}
于 2009-06-05T17:43:07.747 回答