我们已经看到很多关于何时以及为什么使用try/catchtry/catch/finally的问题.我知道try/finally肯定有一个用例(特别是因为它是using语句的实现方式).

我们还看到了the overhead of try/catch and exceptions个问题.

然而,我联系到的问题并不是说最后一次try 的开销.

假设try块中发生的任何事情都没有异常,那么确保finally语句在离开try块时执行(有时通过函数返回)的开销是多少?

再一次,我只要求try/finally,没有catch,没有抛出异常.

谢谢

编辑:好的,我将try 更好地展示我的用例.

我应该用DoWithTryFinally还是DoWithoutTryFinally

public bool DoWithTryFinally()
{
  this.IsBusy = true;

  try
  {
    if (DoLongCheckThatWillNotThrowException())
    {
      this.DebugLogSuccess();
      return true;
    }
    else
    {
      this.ErrorLogFailure();
      return false;
    }
  }
  finally
  {
    this.IsBusy = false;
  }
}

public bool DoWithoutTryFinally()
{
  this.IsBusy = true;

  if (DoLongCheckThatWillNotThrowException())
  {
    this.DebugLogSuccess();

    this.IsBusy = false;
    return true;
  }
  else
  {
    this.ErrorLogFailure();

    this.IsBusy = false;
    return false;
  }
}

这个 case 过于简单化,因为只有两个返回点,但是想象一下如果有四个...或者十个...或者一百.

在某种程度上,我希望使用try/finally,原因如下:

  • 保持干燥原则(尤其是当出口点越来越多时)
  • 如果结果证明我的内部函数没有抛出异常是错误的,那么我想确保将this.Working设置为false.

因此,假设考虑到性能问题、可维护性和干燥原则,对于多少退出点(特别是如果我可以假设所有内部异常都被捕获),我希望招致与try/finally相关的任何性能损失?

编辑#2:我把this.Working的名字改成了this.IsBusy.抱歉,忘了提到这是多线程的(尽管只有一个线程会真正调用该方法);其他线程将进行轮询,以查看对象是否正在执行其工作.如果工作按预期进行,则返回值仅为成功或失败.

推荐答案

为什么不看看你实际得到了什么呢?

下面是C#中的一段简单代码:

    static void Main(string[] args)
    {
        int i = 0;
        try
        {
            i = 1;
            Console.WriteLine(i);
            return;
        }
        finally
        {
            Console.WriteLine("finally.");
        }
    }

下面是调试版本中生成的IL:

.method private hidebysig static void Main(string[] args) cil managed
{
    .entrypoint
    .maxstack 1
    .locals init ([0] int32 i)
    L_0000: nop 
    L_0001: ldc.i4.0 
    L_0002: stloc.0 
    L_0003: nop 
    L_0004: ldc.i4.1 
    L_0005: stloc.0 
    L_0006: ldloc.0 // here's the WriteLine of i 
    L_0007: call void [mscorlib]System.Console::WriteLine(int32)
    L_000c: nop 
    L_000d: leave.s L_001d // this is the flavor of branch that triggers finally
    L_000f: nop 
    L_0010: ldstr "finally."
    L_0015: call void [mscorlib]System.Console::WriteLine(string)
    L_001a: nop 
    L_001b: nop 
    L_001c: endfinally 
    L_001d: nop 
    L_001e: ret 
    .try L_0003 to L_000f finally handler L_000f to L_001d
}

以下是JIT在调试中运行时生成的程序集:

00000000  push        ebp 
00000001  mov         ebp,esp 
00000003  push        edi 
00000004  push        esi 
00000005  push        ebx 
00000006  sub         esp,34h 
00000009  mov         esi,ecx 
0000000b  lea         edi,[ebp-38h] 
0000000e  mov         ecx,0Bh 
00000013  xor         eax,eax 
00000015  rep stos    dword ptr es:[edi] 
00000017  mov         ecx,esi 
00000019  xor         eax,eax 
0000001b  mov         dword ptr [ebp-1Ch],eax 
0000001e  mov         dword ptr [ebp-3Ch],ecx 
00000021  cmp         dword ptr ds:[00288D34h],0 
00000028  je          0000002F 
0000002a  call        59439E21 
0000002f  xor         edx,edx 
00000031  mov         dword ptr [ebp-40h],edx 
00000034  nop 
        int i = 0;
00000035  xor         edx,edx 
00000037  mov         dword ptr [ebp-40h],edx 
        try
        {
0000003a  nop 
            i = 1;
0000003b  mov         dword ptr [ebp-40h],1 
            Console.WriteLine(i);
00000042  mov         ecx,dword ptr [ebp-40h] 
00000045  call        58DB2EA0 
0000004a  nop 
            return;
0000004b  nop 
0000004c  mov         dword ptr [ebp-20h],0 
00000053  mov         dword ptr [ebp-1Ch],0FCh 
0000005a  push        4E1584h 
0000005f  jmp         00000061 
        }
        finally
        {
00000061  nop 
            Console.WriteLine("finally.");
00000062  mov         ecx,dword ptr ds:[036E2088h] 
00000068  call        58DB2DB4 
0000006d  nop 
        }
0000006e  nop 
0000006f  pop         eax 
00000070  jmp         eax 
00000072  nop 
    }
00000073  nop 
00000074  lea         esp,[ebp-0Ch] 
00000077  pop         ebx 
00000078  pop         esi 
00000079  pop         edi 
0000007a  pop         ebp 
0000007b  ret 
0000007c  mov         dword ptr [ebp-1Ch],0 
00000083  jmp         00000072 

现在,如果我注释掉try和finally以及return,我从JIT中得到几乎相同的程序集.您将看到的差异是跳转到finally块,以及一些代码,以确定finally执行后要go 哪里.所以你说的是细微的差别.在发行版中,跳转到finally将得到优化-大括号是nop指令,因此这将成为跳转到下一条指令,这也是一个nop-这是一个简单的窥视孔优化.pop eax和jmp eax同样便宜.

    {
00000000  push        ebp 
00000001  mov         ebp,esp 
00000003  push        edi 
00000004  push        esi 
00000005  push        ebx 
00000006  sub         esp,34h 
00000009  mov         esi,ecx 
0000000b  lea         edi,[ebp-38h] 
0000000e  mov         ecx,0Bh 
00000013  xor         eax,eax 
00000015  rep stos    dword ptr es:[edi] 
00000017  mov         ecx,esi 
00000019  xor         eax,eax 
0000001b  mov         dword ptr [ebp-1Ch],eax 
0000001e  mov         dword ptr [ebp-3Ch],ecx 
00000021  cmp         dword ptr ds:[00198D34h],0 
00000028  je          0000002F 
0000002a  call        59549E21 
0000002f  xor         edx,edx 
00000031  mov         dword ptr [ebp-40h],edx 
00000034  nop 
        int i = 0;
00000035  xor         edx,edx 
00000037  mov         dword ptr [ebp-40h],edx 
        //try
        //{
            i = 1;
0000003a  mov         dword ptr [ebp-40h],1 
            Console.WriteLine(i);
00000041  mov         ecx,dword ptr [ebp-40h] 
00000044  call        58EC2EA0 
00000049  nop 
        //    return;
        //}
        //finally
        //{
            Console.WriteLine("finally.");
0000004a  mov         ecx,dword ptr ds:[034C2088h] 
00000050  call        58EC2DB4 
00000055  nop 
        //}
    }
00000056  nop 
00000057  lea         esp,[ebp-0Ch] 
0000005a  pop         ebx 
0000005b  pop         esi 
0000005c  pop         edi 
0000005d  pop         ebp 
0000005e  ret 

所以你说的是try/finally非常非常小的成本.这一点很重要的问题领域很少.如果你在做memcpy之类的事情,在每个被复制的字节周围放一个try/finally,然后继续复制数百MB的数据,我可以看出这是一个问题,但在大多数情况下?可以忽略不计的

.net相关问答推荐

Docker失败文件找不到

Docker镜像mcr.microsoft.com/dotnet/aspnet:8.0不能在Windows上构建

.Net MAUI Android 无法与 API localhost 对话

无法使用 int.Parse 从字符串转换值

Dictionary.FirstOrDefault() 如何判断是否找到了结果

查找 2 个已知值之间的字符串

无法解析此引用.找不到程序集

string.Format 如何处理空值?

.NET 中是否有一种简单的方法来获得数字的st、nd、rd和th结尾?

是否可以模拟 .NET HttpWebResponse?

简单委托(委托)与多播委托

String 是原始类型吗?

DateTime.TryParseExact() 拒绝有效格式

变量MyException已声明但从未使用

向 .NET 应用程序添加脚本功能

在 .NET 中填充整数列表

从 C# 中的字符串中删除最后一个字符.优雅的方式?

自创建数据库以来,支持ApplicationDbContext上下文的模型已更改

使用 C# 将时间跨度值转换为格式hh:mm Am/Pm

如何为 Dapper 查询动态创建参数