我有几种类似的方法,例如。 CalculatePoint(...)和CalculateListOfPoints(...)。有时,它们可能不会成功,并且需要向呼叫者指出这一点。对于返回通用List的CalculateListOfPoints,我可以返回一个空列表并要求调用者检查这个;但是Point是一个值类型,所以我不能在那里返回null。
理想情况下,我希望这些方法“看起来”相似;一种解决方案可能是将它们定义为
public Point CalculatePoint(... out Boolean boSuccess);
public List<Point> CalculateListOfPoints(... out Boolean boSuccess);
或者返回一个Point?对于CalculatePoint,并返回null以指示失败。这意味着必须回归到非可空类型,这似乎是过度的。
另一种方法是返回布尔boSuccess,将结果(Point或List)作为'out'参数,并将它们称为TryToCalculatePoint或其他...
什么是最佳做法?
编辑:我不想使用Exceptions进行流量控制!有时候会失败。
答案 0 :(得分:24)
我个人认为我会使用与TryParse()相同的想法:使用out参数输出实际值,并返回一个布尔值,指示调用是否成功
public bool CalculatePoint(... out Point result);
我不喜欢为“正常”行为使用异常(如果您希望该函数不适用于某些条目)。
答案 1 :(得分:7)
他们为什么会失败?如果是因为调用者所做的事情(即提供的参数),则抛出ArgumentException是完全合适的。一个避免异常的Try [...]方法很好。
我认为提供引发异常的版本是一个好主意,因此,如果他们错了,那么期望他们将始终提供良好数据的呼叫者将收到适当强烈的消息(即异常)。 / p>
答案 2 :(得分:5)
另一种选择是抛出异常。但是,您通常只想在“例外情况”中抛出异常。
如果失败案例很常见(并非例外),那么您已经列出了两个选项。 编辑:您的项目中可能有一个约定,即如何处理此类非特殊情况(无论是应该返回成功还是对象)。如果没有现有的约定,那么我同意lucasbfr并建议你返回成功(这与TryParse(...)的设计方式一致)。
答案 3 :(得分:2)
如果失败是出于特定原因,那么我认为可以返回null,或者bool并且有一个out参数。但是,如果无论失败都返回null,那么我不推荐它。异常提供了丰富的信息,包括为什么出现故障的原因,如果你回来的都是null,那么你怎么知道它是否因为数据错误,你的内存不足或其他一些奇怪的行为。
即使在.net中,TryParse也有一个Parse兄弟,所以如果你愿意,你可以获得异常。
如果我提供了TrySomething方法,我还会提供一个Something方法,在发生故障时抛出异常。然后由打电话者决定。
答案 4 :(得分:1)
我使用的模型与MS使用的模型与各种类的TryParse方法相同。
您的原始代码:
public Point CalculatePoint(... out Boolean boSuccess);
public List CalculateListOfPoints(... out Boolean boSuccess);
会变成
public bool CalculatePoint(... out(或ref)Point CalculatedValue);
public bool CalculateListOfPoints(... out(或ref)List CalculatedValues);
基本上你将成功/失败作为回报值。
答案 5 :(得分:1)
总结一下,您可以采取以下几种方法:
取决于场景,我倾向于使用返回null或抛出异常的组合,因为它们对我来说似乎是“最干净”并且最适合我工作的公司的现有代码库。所以我个人的最佳实践是方法1和方法2。
答案 6 :(得分:1)
这主要取决于您的方法的行为及其用法。
如果失败是常见且非关键的,那么让您的方法返回一个指示其成功的布尔值,并使用out参数来传达结果。查找哈希中的密钥,在没有数据可用时尝试读取非阻塞套接字上的数据,所有这些示例都属于该类别。
如果意外失败,则直接返回结果并传达异常错误。以只读方式打开文件,连接到TCP服务器,是很好的选择。
有时两种方式都有意义......
答案 7 :(得分:1)
返回 Point.Empty 。当您想要检查结构创建是否成功时,返回特殊字段是一种.NET设计模式。尽可能避免使用 out 参数。
public static readonly Point Empty
答案 8 :(得分:1)
我正在尝试的模式是返回 Maybe 。它具有 TryParse 模式的语义,但与null-return-on-error模式具有类似的签名。
我还不确定这种或那种方式,但我提供它供你集体考虑。它确实具有以下优点:在方法调用之前不需要定义变量以在方法的调用站点处保持out参数。它还可以使用错误或消息集合进行扩展,以指示失败的原因。
Maybe类看起来像这样:
/// <summary>
/// Represents the return value from an operation that might fail
/// </summary>
/// <typeparam name="T"></typeparam>
public struct Maybe<T>
{
T _value;
bool _hasValue;
public Maybe(T value)
{
_value = value;
_hasValue = true;
}
public Maybe()
{
_hasValue = false;
_value = default(T);
}
public bool Success
{
get { return _hasValue; }
}
public T Value
{
get
{ // could throw an exception if _hasValue is false
return _value;
}
}
}
答案 9 :(得分:0)
我认为最佳实践是返回值意味着成功,而exception意味着失败。
我认为您提供的示例中没有任何理由说明在发生故障时您不应该使用exceptions。
答案 10 :(得分:0)
在某些情况下使用异常是一个坏主意(特别是在编写服务器时)。你需要两种方法。另请查看字典类,以了解您应该做什么。
// NB: A bool is the return value.
// This makes it possible to put this beast in if statements.
public bool TryCalculatePoint(... out Point result) { }
public Point CalculatePoint(...)
{
Point result;
if(!TryCalculatePoint(... out result))
throw new BogusPointException();
return result;
}
两全其美!
答案 11 :(得分:0)
bool TrySomething()至少是一种练习,适用于.net的解析方法,但我认为我不喜欢它。
抛出异常通常是一件好事,但它不应该用于您在许多正常情况下会发生的情况,并且它会产生相关的性能成本。
在大多数情况下,当您不想要例外时,尽可能返回null。
然而 - 您的方法有点程序化 - 如何创建类似PointCalculator类的东西 - 将所需数据作为构造函数中的参数?然后在其上调用CalculatePoint,并通过属性访问结果(Point和for Success的单独属性)。
答案 12 :(得分:0)
你不希望在发生预期事件时抛出异常,因为@Kevin声明例外是针对例外情况。
您应该返回“失败”所期望的内容,通常null是我选择的不良回报。
您的方法的文档应告知用户数据无法计算时的预期结果。
答案 13 :(得分:0)
我们曾经写过一个完整的框架,其中所有公共方法都返回true(成功执行)或false(发生错误)。如果我们需要返回一个值,我们使用输出参数。与流行的看法相反,这种编程方式实际上简化了我们的许多代码。
答案 14 :(得分:0)
使用Point,您可以在发生故障时将Point.Empty作为返回值发回。现在这一切真的是返回一个0和X值的点,所以如果它可以是一个有效的返回值,我会远离它,但如果你的方法永远不会返回(0,0)点,那你可以用它。
答案 15 :(得分:0)
抱歉,我只记得Nullable类型,你应该看一下。我不太确定开销是多少。