可以不处理C#方法的返回值吗? 这个例子中的好习惯是什么?
出于好奇…当我们调用一个方法返回一些值,但是我们不处理/使用它时会发生什么? 而且我们也期望有时这个返回值可能会非常大。 价值何去何从? 它甚至创build? 如果是这样,是否有任何性能问题或其他可能发生的问题? (在这种情况下最好的做法是什么?)
假设我们有一些方法可以执行一些数据库操作(插入,更新)并返回DataTable对象中的一些数据。 而且我也知道这个DataTable对象有时可能真的很大:
public static Datatable InsertIntoDB(...) { // executing db command, getting values, creating & returning Datatable object... ... return myDataTable; }
然后当这个方法被使用时,它被这样调用:
DataTable myDataTable = InsertIntoDB(...); // this Datatable object is handled in some way
但有时只是这样的:
InsertIntoDB(...); // returned value not handled; Problem???
我首先想到的是,它认为这个系统足够聪明,可以看到返回的值被忽略,并且不会引起任何问题(这只是简单的发布),但我想确定一下,并且从更有经验的人那里听到更详细的解释这个领域比我。
返回值(或引用,如果它是引用types)被压入堆栈,然后再次popup。
没有比较大的
如果返回值不相关,则可以安全地执行此操作。
但要确保它不相关,以防万一。
这是一些代码:
static string GetSomething() { return "Hello"; } static void Method1() { string result = GetSomething(); } static void Method2() { GetSomething(); }
如果我们看一下IL:
方法一:
.locals init ([0] string result) IL_0000: nop IL_0001: call string ConsoleApplication3.Program::GetSomething() IL_0006: stloc.0 IL_0007: ret
方法2:
IL_0000: nop IL_0001: call string ConsoleApplication3.Program::GetSomething() IL_0006: pop IL_0007: ret
指令数量完全一样。 在Method1中,该值存储在本地string结果(stloc.0)中,该结果超出了作用域时将被删除。 在Method2中,pop操作只是将它从堆栈中移除。
在你返回“很大”的情况下,这些数据已经被创build,并且方法返回一个对它的引用; 而不是数据本身。 在Method1()中,引用被分配给本地variables,垃圾收集器将在variables超出作用域(在这种情况下方法结束)之后对其进行整理。 在Method2()中,垃圾收集器可以在从堆栈中popup引用之后的任何时候开始工作。
通过忽略返回值,如果确实不需要,垃圾收集器可能会更快地开始工作,并释放所有已分配的内存。 但是其中很less(当然在这种情况下),但是通过长时间运行的方法,挂上这些数据可能是一个问题。
但是无论如何,最重要的是要确保你所忽视的回报价值不是你应该采取的行动。
编辑:软化语言非常轻微,澄清。
以我的经验来看,忽略退货价值并不是一个好主意,至less在返回价值是为了传递新信息而不是为了方便的情况下。
我见过的一个例子是好的:
int foo; int.TryParse(someText, out foo); // Keep going
如果someText
包含“0”,或者无法parsing,则foo
将为0。 我们可能不在乎在哪种情况下,方法的回报价值与我们无关。
另一个例子是在字典中 – 假设你正在计算每个string的出现次数。 您可以使用:
int count; dictionary.TryGetValue(word, out count); dictionary[word] = count + 1;
如果单词不在字典中,那么等同于计数为0 – 这是调用TryGetValue
的结果。
作为一个反例,忽略Stream.Read
返回的值(并假设它设法读取所有要求的数据)是一个常见的错误。
如果你不需要返回值,并且需要花费大量的精力进行计算,那么在没有额外的计算的情况下,寻找可以达到相同的期望副作用的东西是值得的,但是不会有额外的性能影响。 我会更担心忽视回报值的正确性而不是performance。
编辑:可以忽略返回值的其他示例:
- 一些stream利的接口,包括
StringBuilder
; 而StringBuilder.Append(x).Append(y);
使用第一个返回值作为第二个调用,通常返回值的调用将被忽略,例如,当附加一个循环 - 一些集合调用可以给返回值,这些值有时会被忽略,例如
HashSet<T>.Add
,它表示实际添加的值还是已经存在的值。 有时你只是不在乎。
但是绝大多数时候,忽略方法的返回值表明它比你需要的要多。
从内存pipe理的angular度来看,这很好 – 如果调用函数不使用它,它会超出范围,并获得垃圾回收。
在这个特殊的情况下, DataTable
确实实现了IDisposable
所以它不是全部都是100%
如果返回的对象实现IDisposable
那么处理它是一个好主意,例如:
using (var retVal = InsertIntoDB(...)) { // Could leave this empty if you wanted }
这取决于它自己的返回值。
编译器会在调用方法中生成这个值,所以如果这个值是IDispolable
或者暴露了Close
方法,或者如果它有资源被释放的话,那么你不应该忽略它并且正确的configuration它,否则你可能会遇到问题内存泄漏
例如,如果返回的值是一个FileStream
并且没有closuresstream,那么在应用程序终止之前该文件可能不会closures,如果应用程序尝试再次打开该文件,那么该文件可能会被closures,它可能会抛出指示“The file正在被另一个进程使用“。 所以你应该小心这种返回的对象, 永远不要忽略它 !
忽略返回值是完全正确的。
然而。 build筑devise,恕我直言,不好。 一个插入方法不应该返回任何东西(除了成功或失败的MAYBE真或假)。 如果需要得到一个新的,更新的数据集,那么应该要求它,即调用其他一些方法来这样做。
如果不使用,则返回的值将被丢弃,但会被创build。 不使用它是完全合理的(虽然你应该是angular力,这是正确的做法),但是如果需要大量的资源来创build,那么这是浪费的。
你可能想要考虑另一种方法是否是更好的select,根本不会创build返回对象。
为了对事物有不同的看法,我认为这种方法应该重新devise。 看看命令 – 查询分离 。
而且, 默默地忽略返回值也不是一个好主意。 代码的读者可能没有作者的原始上下文。 他们可能会认为他忘记了使用它。 如果回报价值不重要,最好明确这个决定:
var ignoredReturnValue = InsertIntoDB(...);
有趣的是,如果你忽略了一个返回值, Nemerle会给你一个警告。 为了不接受警告,你必须明确你的决定,并写下:
_ = InsertIntoDB(...);
我敢肯定,这不会造成任何问题,否则C#将不是一个非常可靠的语言。
我猜测编译器不够聪明来优化这个。 最可能发生的是函数调用中的普通逻辑被执行,例如创build对象并为其分配内存。 如果引用types返回但未捕获,则垃圾回收将再次释放内存。
正如其他人所说,从devise的angular度来看,忽略返回值确实表明了一个问题,很可能你应该看看返回值。
如果你的函数对其他对象做了一些修改(例如一个数据库),我认为如果你不需要处理这个返回的对象,这是可以的。
所有这一切都谈论是否可以忽略返回的types是不需要的,我们无论如何在C#中总是这样做。 你使用的很多函数,就好像它们返回void一样,不会返回void。 想想像Button1.Focus()这样的常用函数,
你知道.Focus()函数返回一个布尔值吗? 如果它成功地把重点放在控制上,它会返回真实的。 所以你可以通过说:
if(Button1.Focus == true)MessageBox.Show(“Button Focused successfully。”); 否则MessageBox.Show(“无法专注于button,抱歉。”);
但通常情况下,你不这样做。 你只是说:Button1.Focus();
你完成了。 我可以给出另外一百个例子,我们忽略返回值,就像函数运行时一样,但是也返回一个对它创build的东西的引用,但是你不关心引用,你只是想让它做这个动作(或者你只是想简单地检查是否有引用或者是否为空)
重点是,即使你不知道,我们总是忽略返回值。