简单的C# Noop语句

59

在C#中,什么是简单的Noop语句,它不需要实现一个方法?(内联/lambda方法可行)

我的当前用例:我想占用try-catch的catch块,以便在调试时可以进入该块并检查异常。
我知道我应该处理/记录异常,但这不是本练习的重点。


5
你能不能把断点放在 catch 块的右括号 } 上? - George Duckett
2
如果你想查看异常,你也可以在 catch (...) 处打断点并单步执行。 - porges
17
@Protector:也许执行流程已经超出了 } 的边缘。你可以尝试使用 catch (Exception e) {{{{{{{{{{ }}}}}}}}}} 来更紧密地控制。 - porges
4
@Protector one,您正在使用发布版还是调试版?在发布版中,catch (Exception e) {} 会被简化为 catch {} - Jonathan Dickinson
2
@RLH,现代应用程序中不应使用NO-OP进行定时。这种模式与多任务处理或移动硬件不兼容(它们会占用其他应用程序无法使用的CPU时间,并导致更高的CPU利用率,浪费电池电力)。 - BrainSlugs83
显示剩余10条评论
18个回答

83

C# 中的标准空语句/空操作是

;

就像这样:

if (true)
    ;

(相关文档)

这个方法非常适合您的使用情况(只需在 ; 行上设置断点,或者以其他方式执行到该行),它是最简化的,并且环境直接支持此目的(因此即使您正在进行复杂的操作,比如查看编译后的源代码,您也不必担心编译器/优化器等会引入其他噪音等)- 并且还具有额外的好处,即发出警告,提醒您在完成调试/推送到生产之后将其从代码中删除。


2
我认为这也是正确的答案。我会添加一个注释,比如 // NOOP,以便让别人知道这是有意为之的。 - gfache
1
一个单独的 ; 只会被编译器忽略,所以从行为上来看它不是一个空操作。它应该什么都不做,而不是什么都不存在。 - Protector one
1
@Protectorone,但你可以在它上面设置断点。 - altair
1
@Protectorone 试试这个:for (int i = 0; i < 2000000000; i++) ; ... 你会发现它会执行几秒钟。所以它不会被编译忽略掉。 - undefined
@nl-x 一个无限循环即使包含一个空操作,仍然是一个无限循环。您的代码提供了循环开销(时间/空间)的非常有用的估计。 - undefined
显示剩余3条评论

81

如果您真的想要“noop”,那么这将定义一个没有名称的动作,它不执行任何操作,然后调用它,导致什么也不发生:

((Action)(() => { }))();

1
你喜欢这个,但是你不喜欢Noop方法?为什么? - David Heffernan
@David Heffernan,因为现在只需要删除一行代码就可以将其丢弃。懒惰?也许吧。 - Protector one
这仍然是一个方法调用。 - Jonathan Dickinson
1
@David Heffernan:如果你只在一个项目上工作,写一次Noop/0当然更好。但如果你在多个代码库上工作,我更喜欢记住一个(相对)简单的语句。 - Protector one
3
答案听起来像是道格拉斯·亚当斯写的。 - Butterkekskrumel
显示剩余2条评论

18

如果你想进入该方法,你可以硬编码一个断点:

System.Diagnostics.Debugger.Break();

如果您不使用发布模式进行编译,以下代码行将会生成 IL 代码,您可以在其上设置断点:

var a = 1;

您还可以编写针对您的计算机特定的Debug.Break():

[Conditional("DEBUG")]
[Obsolete("Please remove me before checkin.")]
public static void Break()
{
    #IF DEBUG
    if (Dns.GetHostName() == "PROTECTORONE")
        Debugger.Break();
    #ENDIF
}

请注意,由于 [Conditional("DEBUG")],该方法在 RELEASE 构建期间的调用站点将不会被调用。


Debugger.Break 对于我的使用情况来说还不错,但如果它被意外提交了,我更希望有一些不会让人感到烦恼的东西。 - Protector one
@Protector,试试那个Break()吧,它有点像黑客技巧。 - Jonathan Dickinson
2
@Protector one - 现在它甚至会在构建期间警告您正在使用它;并且它不会进入RELEASE构建(C#编译器不会调用该方法)。 - Jonathan Dickinson

11

你可以编写一个不执行任何操作的函数。

public static void Noop()
{
}

这违反了“无方法”规则。 - Protector one
9
@Protector,“无方法”规则是武断的,我个人会简单地忽略它。 - David Heffernan
8
@David: 这并不是任意的,因为这是问题的先决条件之一。人们可以质疑它是否是一个“合理”的先决条件,但如果没有这个先决条件,我认为这个问题甚至都不存在。因此,回答应该要么反驳这个先决条件,要么给出与之相匹配的答案 - 而不是像没有被陈述一样违反这个先决条件。 - Jon Skeet
如果您正在调试发布版本,则此内容将被优化掉。 - row1
我更喜欢这种方法。不用管问题规则,这样会更易读、更干净、更易于管理。 - Ibrahim Lawal
显示剩余2条评论

10
在C#中,标准的空语句/无操作是使用;,例如if (true) ;。但是,如果将该标准;作为if语句的分支使用,则MS Visual Studio 2010会显示警告:“可能是错误的空语句”(警告CS0642,尽管VS2010没有告诉我或链接到实际的警告帮助)。更糟糕的是,MSDN C#语言规范并未提到将空语句作为if语句的分支进行编码会引发警告CS0642“可能是错误的空语句”(警告因为它是“不好的形式”,潜在地模棱两可)。
更糟糕的是,看起来VS2010没有提供一种优雅地抑制单个警告的方法。我必须在行之前插入#pragma warning disable CS0642,并在之后[可选]插入#pragma warning disable CS0642。对我来说,这比警告本身还要丑陋。我最好使用{ }代替;。(我可能会使用一个稍微不那么丑陋的覆盖。)
我在这里寻找“C#无操作”,因为我想要一种替代“空语句”的方法,以消除那个警告。我不需要检查点。我只想要一个不像“空语句”那样模棱两可的什么都不做的操作。
这个替代方法不能引发其他警告。int u;不行,因为它会引发警告“变量'u'已声明但从未使用”。int u = 0;不行,因为它会引发警告“变量'u'被赋值,但其值从未使用”。
如果添加noop;(或类似的内容)作为空语句(而不是宏定义),那就太好了。
如果noop();(或类似的函数)是一个空体的函数(当编译器内联时可以完全消失),那将几乎是很好的。
当分支只有一条语句时,我经常省略周围的{}行,因为它们是不必要的,并且会使代码在垂直方向上拉伸,使其更难阅读。语言中的不一致性在于,当它们包围零个语句时,我不能省略周围的{}行。我可以将这两行压缩成同一行上的{ },但这是不一致的。我认为在一行上使用;是最整洁的解决方案,并且它不应该因“不良形式”的[未声明]理由而引发警告。我认为警告CS0642应默认关闭。我认为以下代码应该被接受:
if (condition1)
  action1;
else if (condition2)
  ;  // (do nothing)
else if (condition3)
  action3;
else if (condition4)
  ;  // (do nothing)
else if (condition5)
  action5;
else
  action99;

7
您可以直接写:
catch {
    ;
}

单个分号的空语句是C#中的NOOP。

6
如何呢:
GC.KeepAlive(e);

在这里,e 是异常变量?

(我尚未尝试在 catch 声明本身上设置断点。感觉应该可以做到,就是为了这个原因。但它是否有效是另一回事。)

或者更加含蓄地说,假设您已经针对 System.LINQ 有了 using 指令:

"".AsEnumerable();

1
但是,根据一个非常聪明的人在这里发表的评论:“这将使任何值类型的值变成箱形”,请参阅:https://dev59.com/plnUa4cB1Zd3GeqPWgAH - Protector one
啊,是的,相当尴尬...但如果我能在更一般的情况下使用这个noop就好了。 - Protector one
@Jon Skeet 在调试中,e 的生命周期会延长到方法的结束,那么为什么需要 KeepAlive? - Felice Pollano
1
@Felice:正是因为它不会产生任何影响。问题正在“询问”如何简单地什么都不做。我认为GC.KeepAlive非常适合这个要求。 - Jon Skeet
@Protector one:如果你想要一个更通用的空操作,最好写一个单独的方法来实现。 - Jon Skeet

5

除了直接回答问题的答案外。


如果您只是想要中断,那么您可以将断点放置在catch块的开头{或结尾}处。


这对我不起作用...当我这样做时,Visual Studio从未初始化异常变量。 - Protector one
尝试打开 { 大括号。我可以在空的 catch 块中看到异常。当然,你需要写 catch(Exception e),并接受未使用的警告。 - H H
好的,当您将构建配置设置为Debug时,这确实可以工作。抱歉。 - Protector one

5

我知道这是一个老问题,技术上说,这个答案与提问者的用例无关。然而,在CIL中有一个NOOP指令,即nop。作为一个实验,可以参考下面的CIL应用程序。

.assembly extern mscorlib {}

.assembly Test
{
    .ver 1:0:1:0
}
.module test.exe

.method static void main() cil managed
{
    .maxstack 1
    .entrypoint

    nop
    nop
    nop
    nop

    ret
}

如果您编译应用程序并使用类似ILSpy的工具将其反编译为C#,则main()方法的内容如下:
static void main()
{
}

正如您所看到的,这里什么也没有。然而,如果我们想要验证CIL编译器没有优化掉这些nop语句,我们可以在ILSpy中查看我们的应用程序反编译后的IL代码,这是主方法的内容:

.method static privatescope 
    void main$PST06000001 () cil managed 
{
    // Method begins at RVA 0x2050
    // Code size 5 (0x5)
    .maxstack 1
    .entrypoint

    IL_0000: nop
    IL_0001: nop
    IL_0002: nop
    IL_0003: nop
    IL_0004: ret
} // end of method '<Module>'::main

CIL会将nop指令编译成汇编代码。由于C#没有实现这个指令,所以在反汇编的C#代码中不会显示这些nop命令。

我没有Reflector的许可证,但我想如果你用Reflector反编译这些二进制文件,你会得到类似的C#输出。


4

在考虑了各种选项后,我决定使用一对空括号和一个简短的注释。没有多余的代码,自我说明,并且比单个分号更加明显。

{/* Noop */}

e.g.

if (String.IsNullOrEmpty(value))
    {/* Noop */}
else if (Data.ContainsKey(key))

我认为这是最好的解决方案。任何开发者看到这一行代码时都会立即知道意图并且不会误解。被接受的答案中的解决方案太过混乱,而通常被接受的 noop(一个孤立的分号)并不明显是有意为之。这应该成为我们所有人使用的标准 noop。 - Don Shrout

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接