Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/391.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 一般函数问题(C+;+;/Java/C)_C#_Java_C++_Function_Prerequisites - Fatal编程技术网

C# 一般函数问题(C+;+;/Java/C)

C# 一般函数问题(C+;+;/Java/C),c#,java,c++,function,prerequisites,C#,Java,C++,Function,Prerequisites,这个问题可能与语言无关,但我将重点讨论指定的语言 在处理一些遗留代码时,我经常看到函数的示例,这些函数(在我看来,显然)在它们内部做了太多的工作。我说的不是5000个LoC怪物,而是函数,它们在它们内部实现先决条件检查 下面是一个小例子: 现在-应该如何处理以下问题? 一般来说,与此类似-此函数是否应该只执行要求的操作,并将“先决条件检查”移动到调用方: if (argument1 != null && argument2 + argument3 < 0 &&am

这个问题可能与语言无关,但我将重点讨论指定的语言

在处理一些遗留代码时,我经常看到函数的示例,这些函数(在我看来,显然)在它们内部做了太多的工作。我说的不是5000个LoC怪物,而是函数,它们在它们内部实现先决条件检查

下面是一个小例子:


现在-应该如何处理以下问题?

一般来说,与此类似-此函数是否应该只执行要求的操作,并将“先决条件检查”移动到调用方:

if (argument1 != null && argument2 + argument3 < 0 && ...) {
   // Now all the checks inside can be removed.
   NotWorriedFunction();
}

我不确定这个问题是否可以推广,但我还是想谈谈你对这个问题的看法——也许我可以重新思考一下。

如果您有其他解决方案,请随时告诉我:)

谢谢。

“或者-它应该只是针对每个先决条件不匹配抛出异常吗?”


是的。

这是一个有趣的问题。检查“”的概念,您可能会发现它很有用。

您应该在调用和函数之前进行检查,如果您拥有该函数,则应该使其在传递的参数不符合预期时引发异常

在调用代码中,应该处理这些异常。当然,传递的参数应该在调用之前进行验证。

这取决于具体情况

我想把我的答案分为案例1、案例3和案例2

案例1、3

如果可以安全地从参数问题中恢复,请不要抛出异常。一个很好的例子是
TryParse
方法-如果输入格式错误,它们只返回
false
。另一个例子(对于异常)是所有LINQ方法-如果
null
或其中一个必需的
Func
null
,它们就会抛出。但是,如果他们接受自定义的
IEqualityComparer
IComparer
,他们不会抛出,只需使用
EqualityComparer.default
Comparer.default
的默认实现。这完全取决于参数的使用上下文,以及您是否能够安全地从中恢复

案例2

我只会在代码位于类似于基础设施的环境中时使用这种方法。最近,我开始重新实现LINQ堆栈,您必须实现几个接口-这些实现永远不会在我自己的类和方法之外使用,因此您可以在它们内部进行假设-外部将始终通过接口访问它们,并且不能自己创建它们


如果你对API方法做出这样的假设,你的代码会在错误的输入上抛出各种异常,用户不知道发生了什么,因为他不知道你的方法的内部。

每个函数/方法/代码块都应该有一个,以及后置条件,即函数返回时的世界状态。这些帮助您的程序员同事理解您的意图

根据定义,如果先决条件为false,则代码不应工作;如果后置条件为false,则代码被视为有缺陷

无论您是在头脑中、在设计文档的纸上、在注释中还是在实际的代码检查中写下这些内容,都是一种品味问题

但是,如果您将前置条件和后置条件编码为显式检查,那么从长远来看,一个实际的问题就更容易解决。如果您编写了这样的检查代码,因为该代码预计无法工作 如果先决条件是错误的,或者后决条件是错误的,那么前置条件和后置条件检查应该会导致程序以一种易于发现故障点的方式报告错误。代码不应该做的只是“返回”什么也没做,正如您的示例所示,因为这意味着它以某种方式正确执行。 (当然,代码可能被定义为什么也不做就退出,但如果是这样的话,前置和后置条件应该反映这一点。)

显然,您可以使用if语句编写此类检查(您的示例非常接近):

但通常,在代码中通过定义特殊的函数/宏/语句来指示前置或后置条件的存在。。。对于称为断言的语言,当断言为false时,这种特殊构造通常会导致程序中止和回溯

代码的编写方式如下所示:

void WorriedFunction(...)  
 {    assert(argument1 != null); // fail/abort if false [I think your example had the test backwards]
      assert(argument2 + argument3 >= 0);
      assert(!stateManager.currentlyDrawing());
      /* body of function goes here */ 
 }  

复杂的函数可能愿意告诉调用方某些条件已失败。这就是例外的真正目的。如果存在异常,从技术上讲,后置条件应该说明“函数可能在条件xyz下以异常退出”。

虽然语言特定,但我认为您可能会感兴趣。有一天,当IDE变得更加直观(如),我认为这些设计合同可以通过双击方法并将其输入文本框(或者更好,从下拉菜单中选择)来指定。这样,他们不仅不会再把代码弄得乱七八糟(他们真的不应该这样),而且文档生成也将是自动的!我并不完全同意这一点。有值得注意的例外情况,例如
TryParse
方法。@Femaref:在
TryParse
的情况下,“输入是可解析的”并不是一个先决条件。我同意,尽管我不打算对一个你显然没有努力写的答案投反对票。也许你可以向他解释一下为什么这被认为是最佳实践(以及为什么最好不要强迫打电话的人验证)?@Femaref:顾名思义,TryParse会尝试做一些事情,并告诉你是否成功。它不会告诉内部处理了哪些异常。例如,让我们试试Int32.TryParse。此方法返回一个32位有符号整数
if (argument1 != null && argument2 + argument3 < 0 && ...) {
   // Now all the checks inside can be removed.
   NotWorriedFunction();
}
if (argument1 != null) throw NullArgumentException;
if (!precondition) die("Precondition failure in WorriedFunction"); // die never comes back
void WorriedFunction(...)  
 {    assert(argument1 != null); // fail/abort if false [I think your example had the test backwards]
      assert(argument2 + argument3 >= 0);
      assert(!stateManager.currentlyDrawing());
      /* body of function goes here */ 
 }