这是不错的C#风格?

时间:2009-08-13 20:00:58

标签: c# coding-style

考虑以下方法签名:

public static bool TryGetPolls(out List<Poll> polls, out string errorMessage)

此方法执行以下操作:

  • 访问数据库以生成Poll对象列表。
  • 如果成功则返回true,errorMessage将是空字符串
  • 如果不成功则返回false,errorMessage将包含异常消息。

这是好风格吗?

更新: 假设我确实使用以下方法签名:

public static List<Poll> GetPolls()

并且在该方法中,它不会捕获任何异常(因此我依赖调用者来捕获异常)。如何处置和关闭该方法范围内的所有对象?抛出异常后,关闭并处置方法中的对象的代码将无法再访问。

17 个答案:

答案 0 :(得分:43)

该方法试图做三件事:

  1. 检索并返回民意调查清单
  2. 返回表示成功的布尔值
  3. 返回错误消息
  4. 从设计的角度来看,这非常混乱。

    更好的方法是简单地声明:

    public static List<Poll> GetPolls()
    

    如果出现任何问题,请让此方法抛出Exception

答案 1 :(得分:11)

我相信

public static bool TryGetPolls(out List<Poll> polls)

会更合适。如果方法是TryGet,那么我最初的假设是有理由期望它失败,并且调用者有责任确定下一步该做什么。如果调用者没有处理错误,或者想要错误信息,我希望他们可以调用相应的Get方法。

答案 2 :(得分:11)

这绝对不是写C#的惯用方式,这也意味着它可能也不是一个好的风格。

如果你有一个TryGetPolls方法,那么这意味着如果操作成功你想要结果,如果没有,那么你不关心为什么它不成功。

当你只有GetPolls方法时,它意味着你总是想要结果,如果它没有成功,那么你想知道为什么以Exception的形式。

将两者混合介于两者之间,这对大多数人来说都是不寻常的。所以我要么说要么不返回错误信息,要么在失败时抛出Exception,但不要使用这种奇怪的混合方法。

所以你的方法签名应该是:

IList<Poll> GetPolls();

bool TryGetPolls(out IList<Poll> polls);

(请注意,我在两种情况下都返回IList<Poll>而不是List<Poll>,因为编程到抽象而不是实现也是一种好习惯。)

答案 3 :(得分:7)

作为一般规则,我会说不。

我拒绝的原因实际上并不是因为您正在执行TryGetX并返回带有bool参数的out。我认为这样做很糟糕,因为你还会返回一个错误字符串。

Try应该只忽略一个常见的特定错误。其他问题可能仍会在适当的异常消息中引发异常。请记住,像这样的Try方法的目标是当您期望特定的,单一类型的失败更频繁地发生时,避免抛出异常的开销。

相反,你要找的是一对方法:

public static bool TryGetPolls( out List<Poll> polls );
public static List<Poll> GetPolls();

通过这种方式,用户可以执行适当的操作,GetPolls可以TryGetPolls实现。我假设你的static在上下文中是有意义的。

答案 4 :(得分:7)

考虑回复:

  • 空集合

对我而言,多个输出参数是code smell。该方法应该只做一件事。

考虑使用以下方式提出和处理错误消息:

throw new Exception("Something bad happened");
//OR
throw new SomethingBadHappenedException();

答案 5 :(得分:2)

不,从我的观点来看,这是非常糟糕的风格。我会这样写:

public static List<Poll> GetPolls();

如果调用失败,则抛出异常并将错误消息放入异常中。这就是例外情况,您的代码将变得更清晰,更易读,更易于维护。

答案 6 :(得分:1)

这取决于错误消息是什么。例如,如果由于数据库连接不可用而无法继续处理等,那么您应该像其他人提到的那样抛出异常。

但是,您可能只想返回有关尝试的“元”信息,在这种情况下,您只需要一种方法从单个方法调用中返回多条信息。在这种情况下,我建议创建一个包含两个属性的PollResponse类:List&lt;投票&gt;轮询和字符串ErrorMessage。然后让你的方法返回一个PollResponse对象:

class PollResponse
{
    public List<Poll> Polls { get; }
    public string MetaInformation { get; }
}

答案 7 :(得分:1)

指南说如果不是绝对必要的话,试着避免 ref out 参数,因为它们会使API更难使用(不再需要链接方法,开发人员必须在调用方法之前声明所有变量)

同样返回错误代码或消息不是最佳做法,最佳做法是使用例外和异常处理进行错误报告,否则错误将变得容易被忽略,并且还有更多工作传递错误信息周围,同时丢失有价值的信息,如堆栈跟踪或内部异常。

声明方法的更好方法是这样的。

public static List<Poll> GetPolls() ... 

并且用于错误报告使用异常处理

try 
{
   var pols = GetPols();
   ...
} catch (DbException ex) {
   ... // handle exception providing info to the user or logging it.
}

答案 8 :(得分:1)

不是 - 我可以看到很多问题。

首先,这种方法听起来像你通常期望它成功;错误(无法连接到数据库,无法访问polls表等)将是罕见的。在这种情况下,使用异常报告错误更为合理。 Try...模式适用于您经常期望呼叫“失败”的情况 - 例如在将字符串解析为整数时,很可能字符串是用户输入可能无效,因此您需要有一种快速处理方法 - 因此TryParse。这不是这种情况。

其次,您将错误报告为bool值,指示是否存在错误,以及字符串消息。调用者如何区分各种错误呢?他当然无法匹配错误消息文本 - 这是一个可以更改的实现细节,可以进行本地化。在“无法连接到数据库”之类的东西之间可能存在天壤之别(可能只是在这种情况下打开数据库连接设置对话框并让用户编辑它?)和“连接到数据库,但它说'拒绝访问' ”。您的API没有很好的方法来区分它们。

总结一下:使用例外而不是bool + out string来报告消息。完成后,您只需使用List<Poll>作为返回值,而无需out参数。当然,将方法重命名为GetPolls,因为Try...是为bool+out模式保留的。

答案 9 :(得分:0)

我在这里错过了什么吗?问题提示者似乎想知道如果方法失败,如何清理资源。

public static IList<Poll> GetPolls()
{
    try
    {
    }
    finally
    {
        // check that the connection happened before exception was thrown
        // dispose if necessary
        // the exception will still be presented to the caller
        // and the program has been set back into a stable state
    }
}

在设计方面,我会考虑将此方法推送到存储库类中,以便您可以使用某种上下文来理解该方法。据推测,整个应用程序不负责存储和获取民意调查:这应该是数据存储的责任。

答案 10 :(得分:0)

还有这种模式,如许多Win32函数所示。

public static bool GetPolls(out List<Poll> polls)


if(!PollStuff.GetPolls(out myPolls))
  string errorMessage = PollStuff.GetLastError();

但IMO太可怕了。 除非这种方法必须在3D游戏物理引擎中运行65次或者某种方式,否则我会选择基于异常的东西。

答案 11 :(得分:0)

请说明您的假设,约束,愿望/目标和推理;我们不得不猜测和/或阅读你的想法以了解你的意图。

假设你想要你的功能

  • 创建民意调查清单对象
  • 禁止所有例外
  • 表示使用布尔值
  • 成功
  • 并在失败时提供可选的错误消息

然后上面的签名很好(尽管吞下所有可能的例外并不是一个好习惯)。

作为一种通用的编码方式,它有一些潜在的问题,正如其他人所提到的那样。

答案 12 :(得分:0)

C#方法应该只做一件事。你试图用这种方法做三件事。我会像其他人建议的那样做,并在出现错误时抛出异常。另一种选择是为List对象创建扩展方法。

e.g。在公共静态类中:

public static List<Poll> Fill( this List<Poll> polls) {
   // code to retrieve polls
}

然后,要调用它,你会做类似的事情:

List<Poll> polls = new List<Poll>().Fill();
if(polls != null)
{
  // no errors occur
}

编辑:我刚刚做了这个。您可能需要或不需要List<Poll>().Fill()

中的新运算符

答案 13 :(得分:0)

我觉得很好。我更喜欢这样:

enum FailureReasons {}
public static IEnumerable<Poll> TryGetPolls(out FailureReasons reason)

因此错误字符串不存在于数据访问代码中......

答案 14 :(得分:0)

我会这样重申。

public static List<Poll> GetPolls()
{
    ...
}

如果它无法检索轮询,它应该抛出一个异常(errorMessage),而且这允许方法链接,这比处理参数更简单。

如果您运行FxCop,则需要将List更改为IList以使其满意。

答案 15 :(得分:0)

这取决于方法失败的频率。通常,.Net中的错误应与Exception进行通信。该规则不成立的情况是错误条件频繁发生,throw和异常的性能影响过高。

对于数据库类型的工作,我认为Exception是最好的。

答案 16 :(得分:0)

取决于错误是否常见,或者我们是否真的是例外。

如果错误非常罕见且不好,那么您可能需要考虑让方法只返回轮询列表并在发生错误时抛出异常。

如果错误是正常操作中非常常见的部分,就像在int.TryParse方法中将字符串转换为整数的错误一样,您创建的方法会更合适。

我猜你的前者可能是最适合你的。