我最近和一个伙伴讨论过返回值只有一个含义。在我之前的工作中,我们使用C ++并使用了typedef'ed 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 :(得分:5)
返回值和错误消息在技术上不绑定在一起。您可以让开发人员稍后出现并向IsTestReady添加新的故障条件,并且该故障情况可能不会设置错误消息。或者,也许有消息,但它并不完全代表失败(例如,可能是警告或其他内容),因此错误消息参数可能已设置,但返回值为true。
在这种情况下,异常并不真正起作用,因为StriplingWarrior在他的评论中写的确切原因 - 异常应该用于非正常操作状态,而非就绪测试是正常状态。
一种解决方案可能是删除错误消息参数并让IsTestReady函数返回一个类:
public class TestReadyResult {
public bool IsReady { get; set; }
public string Error { get; set; }
}
只有一个要检查的属性 - TestReadyResult.IsReady - 用于测试状态,如果需要,可以将Error属性用于非就绪状态。也没有额外的参数来管理函数调用。
答案 1 :(得分:3)
我不喜欢使用null或null返回值表示没有错。比你给出的更好的比较是:
class Person
{
public int Height {get;}
public bool IsBorn() { return Height > 0; }
}
在.NET中,通常的做法是使用您在原始方法中看到的“bool return with out parameter”模式(例如,参见各种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
方法?
答案 2 :(得分:0)
我会使用异常来传达有关失败状态的信息,而不是依赖调用者知道如何使用错误消息字段(即使它是私有的)。