配色: 字号:
C# 异常处理(Catch Throw)IL分析
2016-09-29 | 阅:  转:  |  分享 
  
C#异常处理(CatchThrow)IL分析

1、catchthrow的几种形式及性能影响:

[csharp]viewplaincopy

privatevoidForm1_Click(objectsender,EventArgse)

{

try

{



}

catch

{

throw;

}

}

privatevoidForm1_Load(objectsender,EventArgse)

{

try

{



}

catch(Exception)

{

throw;

}

}

privatevoidForm1_Enter(objectsender,EventArgse)

{

try

{



}

catch(Exceptionee)

{

throw;

}

}

privatevoidForm1_DoubleClick(objectsender,EventArgse)

{

try

{



}

catch(Exceptionee)

{

throwee;

}

}

对应的IL代码(以下代码是release版本的IL代码):

[plain]viewplaincopy

.methodprivatehidebysiginstancevoidForm1_Click(objectsender,

class[mscorlib]System.EventArgse)cilmanaged

{

//代码大小1(0x1)

.maxstack8

IL_0000:ret

}//endofmethodForm1::Form1_Click



.methodprivatehidebysiginstancevoidForm1_Load(objectsender,

class[mscorlib]System.EventArgse)cilmanaged

{

//代码大小1(0x1)

.maxstack8

IL_0000:ret

}//endofmethodForm1::Form1_Load



.methodprivatehidebysiginstancevoidForm1_Enter(objectsender,

class[mscorlib]System.EventArgse)cilmanaged

{

//代码大小1(0x1)

.maxstack8

IL_0000:ret

}//endofmethodForm1::Form1_Enter



.methodprivatehidebysiginstancevoidForm1_DoubleClick(objectsender,

class[mscorlib]System.EventArgse)cilmanaged

{

//代码大小1(0x1)

.maxstack1

.localsinit([0]class[mscorlib]System.Exceptionee)

IL_0000:ret

}//endofmethodForm1::Form1_DoubleClick

可以看到Form1_Click、Form1_Load、Form1_Enter中的trycatch已经被编译器优化掉了:

[plain]viewplaincopy

IL_0000:ret//即为return标记返回值

只有Form1_DoubleClick中的trycatch中对trycatch进行了处理:

[plain]viewplaincopy

.localsinit([0]class[mscorlib]System.Exceptionee)//定义Exception类型参数ee(此时已经把ee存入了CallStack中)

即在Form1_DoubleClick中的trycatch才会对性能产生影响。

==》可以看出一下三种trycatch的写法对于release版本的代码来说是完全一样,也不会产生任何的性能消耗:

[csharp]viewplaincopy

try

{



}

catch

{

throw;

}

try

{



}

catch(Exception)

{

throw;

}

try

{



}

catch(Exceptionee)

{

throw;

}

对于上面的结论大家可以写测试demo验证一下(已测试,结果与分析一致偷笑)。

那么对于debug模式下的IL代码是什么样子的呢?

[plain]viewplaincopy

.methodprivatehidebysiginstancevoidForm1_Click(objectsender,

class[mscorlib]System.EventArgse)cilmanaged

{

//代码大小11(0xb)

.maxstack1

IL_0000:nop

.try

{

IL_0001:nop

IL_0002:nop

IL_0003:leave.sIL_0009

}//end.try

catch[mscorlib]System.Object

{

IL_0005:pop

IL_0006:nop

IL_0007:rethrow

}//endhandler

IL_0009:nop

IL_000a:ret

}//endofmethodForm1::Form1_Click



.methodprivatehidebysiginstancevoidForm1_Load(objectsender,

class[mscorlib]System.EventArgse)cilmanaged

{

//代码大小11(0xb)

.maxstack1

IL_0000:nop

.try

{

IL_0001:nop

IL_0002:nop

IL_0003:leave.sIL_0009

}//end.try

catch[mscorlib]System.Exception

{

IL_0005:pop

IL_0006:nop

IL_0007:rethrow

}//endhandler

IL_0009:nop

IL_000a:ret

}//endofmethodForm1::Form1_Load



.methodprivatehidebysiginstancevoidForm1_Enter(objectsender,

class[mscorlib]Systewww.sm136.comm.EventArgse)cilmanaged

{

//代码大小11(0xb)

.maxstack1

.localsinit([0]class[mscorlib]System.Exceptionee)

IL_0000:nop

.try

{

IL_0001:nop

IL_0002:nop

IL_0003:leave.sIL_0009

}//end.try

catch[mscorlib]System.Exception

{

IL_0005:stloc.0

IL_0006:nop

IL_0007:rethrow

}//endhandler

IL_0009:nop

IL_000a:ret

}//endofmethodForm1::Form1_Enter



.methodprivatehidebysiginstancevoidForm1_DoubleClick(objectsender,

class[mscorlib]System.EventArgse)cilmanaged

{

//代码大小11(0xb)

.maxstack1

.localsinit([0]class[mscorlib]System.Exceptionee)

IL_0000:nop

.try

{

IL_0001:nop

IL_0002:nop

IL_0003:leave.sIL_0009

}//end.try

catch[mscorlib]System.Exception

{

IL_0005:stloc.0

IL_0006:nop

IL_0007:ldloc.0

IL_0008:throw

}//endhandler

IL_0009:nop

IL_000a:ret

}//endofmethodForm1::Form1_DoubleClick

可以看出四种写法在debug模式下区别只是:rethrow与throw的区别。IL中rethrow与throw分别代表啥呢?

Throw:引发当前位于计算堆栈上的异常对象。

Rethrow:再次引发当前异常。

即当我们抛出一个异常时,CLR会重新设置一个异常起始点。CLR只记录最近一次异常抛出的位置。下面代码抛出一个异常,从而导致CLR重新设置该异常的起始点:

[csharp]viewplaincopy

try

{

//一些处理

}

catch(Exceptione)

{

//一些处理

throwe;//CLR认为这里是异常的起始点

}

相反,如果我们抛出一个异常对象,CLR将不会重新设置其堆栈的起始点,下面代码抛出一个异常,但不会导致CLR重新设置异常的起始点:

[csharp]viewplaincopy

try

{

//一些处理

}

catch(Exceptione)

{

//一些处理

throw;//CLR不会重新设置异常的起始点

}

C#中使用throw和throwex抛出异常,但二者是有区别的。

在C#中推荐使用throw;来抛出异常;throwex;会将到现在为止的所有信息清空,认为你catch到的异常已经被处理了,只不过处理过程中又抛出新的异常,从而找不到真正的错误源。

throwe重新抛出异常,并非转发原来的异常,而会更改包括StackTrace在内的许多异常内部信息;对于调用连很深情况,性能损耗超出想象。

献花(0)
+1
(本文系网络学习天...首藏)