2011-06-08 69 views
4

我最近与一位朋友谈论返回值只取一个意思。在我以前的工作中,我们使用C++并且使用了typedef处理的wBOOL,所以0表示wFALSE,1表示wTRUE。建筑师说,我们也可以返回2,3,4 ......以获取更多信息,我认为这是一个可怕的想法。如果我们期望wTRUE = 1和wFALSE = 0且wBOOL = {wTRUE,wFALSE},则应避免返回任何其他内容......现在转到今天的C#。使用返回的错误消息,以确定是否存在错误

我最近审查了一段代码,那里有功能的集合,确定是否有错误,返回的字符串返回给用户:

private bool IsTestReady(out string errorMessage) 
{ 
    bool isReady = true; 
    errorMessage = string.Empty; 
    if(FailureCondition1) 
    { 
    isReady = false; 
    errorMessage = FailureMessage1; 
    } 
    else if(FailureCondition2) 
    { 
    isReady = false; 
    errorMessage = FailureMessage2; 
    } 
    //... other conditions 
    return isReady; 
} 

然后,要使用这些功能...

private enum Tests 
{ TestA, TestB, TestC } 
private void UpdateUI() 
{ 
    string error = string.Empty; 
    bool isTestReady; 
    switch(this.runningTest) // which test are we running (TestA, TestB, or TestC) 
    { 
    case Tests.TestA: 
     isTestReady = IsTestAReady(error); 
     break; 
    case Tests.TestB: 
     isTestReady = IsTestBReady(error); 
     break; 
    case Tests.TestC: 
     isTestReady = IsTestCReady(error); 
     break; 
    } 
    runTestButton.Enabled = isTestReady; 
    runTestLabel.Text = error; 
} 

我认为这些出分成两种方法:

private string GetTestAErrorMessage() 
{ 
    //same as IsTestReady, but only returns the error string, no boolean stuffs 
} 

private bool IsTestAReady 
{ 
    get{ return string.IsNullOrEmpty(GetTestAErrorMessage()); } 
} 

这是否违背了不具有返回值意味着不止一件事的原则?例如,在这种情况下,如果出现错误消息IsNullOrEmpty,那么就没有错误。我认为这不违反该委托人;我的合作确实如此。对我来说,这是没有什么不同:

class Person 
{ 
    public int Height {get;} 
    public bool IsTall() { return Height > 10; } 
} 

对这个问题的不同方法的任何想法或建议?我认为out参数是最差的解决方案。

+0

顺便说一句,我没有添加关于Person类的最后一点,它与我不一样。在这种情况下,高度的存在并不意味着天生的;但是错误信息的存在确实意味着存在错误。 – MPavlak 2012-08-27 14:07:44

回答

3

我不喜欢让null或空的返回值表示没有错。比你给了一个更好的比较是:

class Person 
{ 
    public int Height {get;} 
    public bool IsBorn() { return Height > 0; } 
} 

在.NET中,它是使用你看到“布尔与输出参数返回”模式在你原来的方法(请参阅各种TryParse方法常见的做法,为例)。但是,如果您愿意,另一种解决方案是创建一个TestReadyCheck类,同时使用布尔值和字符串作为属性。我和以下班级做过类似的事情,对此很满意。

public class RequestFilterResult 
{ 
    public static readonly RequestFilterResult Allow = new RequestFilterResult(true, null); 
    public static RequestFilterResult Deny(string reason) { return new RequestFilterResult(false, reason); } 
    protected RequestFilterResult(bool allowRequest, string denialReason) 
    { 
     AllowRequest = allowRequest; 
     DenialReason = denialReason; 
    } 

    public bool AllowRequest { get; private set; } 
    public string DenialReason { get; private set; } 
} 

这允许以下用法:

public RequestFilterResult Filter(...) 
{ 
    if (FailureCondition1) return RequestFilterResult.Deny(FailureMessage1); 
    if (FailureCondition2) return RequestFilterResult.Deny(FailureMessage2); 
    return RequestFilterResult.Allow(); 
} 

它的简洁,同时实施的是失败的结果提供了一个失败的消息,并且成功的结果没有。

在附注中,您的switch声明的结构感觉像是一种代码味道。你可能想要考虑利用多态的方法。也许让每个测试都有自己的类,其上有一个IsTestReady的方法?

+0

“在.NET中,通常使用'bool return with out参数'模式,您会看到” - 这是真的吗?我认为TryParse等是方便的方法,因为它们会抛出。如果替代方法(Parse)没有抛出,那么TryParse就没有任何理由 - 这至少是我的理解。 – MPavlak 2011-06-09 13:54:14

+0

非常感谢,我真的很喜欢你的想法,使用公开的静态方法预先打包Deny和Allow版本的结果。我也同意代码的味道 - 感谢您的帮助! – MPavlak 2011-06-09 13:57:13

+0

@ user652688:关于'TryParse',有些情况下你期望字符串是有效的,所以如果你的期望没有被满足,你宁愿抛出一个异常,但是还有其他的情况你期望字符串可能不是有效的,并且宁愿在一个操作中检查和执行解析(因为它基本上是相同的逻辑 - 没有意义做两次)。通过返回一个bool,该方法很好地适用于'if(int.TryParse(...)){}'结构。你的代码不使用这个结构,但是可以进行很好的重构。 (例如,您可以避免在成功时设置错误标签) – StriplingWarrior 2011-06-09 14:57:43

0

我会使用异常来传达有关失败状态的信息,而不是依赖调用者知道如何使用错误消息字段(即使它是私人的)。

+0

如果这确实是一个例外状态,我会同意。在这种情况下,它看起来更像是测试尚未准备好运行的预期情况。 – StriplingWarrior 2011-06-08 18:52:58

+0

我同意,因为这不是例外情况不适合的特例。 – MPavlak 2011-06-09 13:54:41

5

返回值和错误消息在技术上不绑定在一起。您可以让开发人员稍后再来,并为IsTestReady添加新的故障条件,并且该故障条件可能不会设置错误消息。或者,也许有消息,但它并不完全代表失败(如可能是警告或某事),所以错误消息参数可能会被设置,但返回值为true。

在这种情况下,由于StriplingWarrior在其评论中写道的确切原因,异常不起作用 - 异常应该用于非正常运行状态,而未准备好的测试是正常状态。

一种解决方案是去除错误信息参数,并有IsTestReady函数返回一个类:

public class TestReadyResult { 
    public bool IsReady { get; set; } 
    public string Error { get; set; } 
} 

只有一个属性来检查 - TestReadyResult.IsReady - 用于测试状态,并在必要时, Error属性可用于非就绪状态。没有额外的参数来管理函数调用。

+0

谢谢,我想知道为什么我厌恶创建类似这样的返回类型,但我确实认为这可能是最合适的解决方案。 – MPavlak 2011-06-09 13:55:54