1. 下面的代码,1和2有什么不同:
void Test()
{
// 1.
try { }
catch (Exception) { }
// 2.
try { }
catch { }
}
如果用ildasm查看:
.method private hidebysig instance void Test() cil managed
{
// Code size 22 (0x16)
.maxstack 1
IL_0000: nop
.try
{
IL_0001: nop
IL_0002: nop
IL_0003: leave.s IL_000a
} // end .try
catch [mscorlib]System.Exception
{
IL_0005: pop
IL_0006: nop
IL_0007: nop
IL_0008: leave.s IL_000a
} // end handler
IL_000a: nop
.try
{
IL_000b: nop
IL_000c: nop
IL_000d: leave.s IL_0014
} // end .try
catch [mscorlib]System.Object
{
IL_000f: pop
IL_0010: nop
IL_0011: nop
IL_0012: leave.s IL_0014
} // end handler
IL_0014: nop
IL_0015: ret
} // end of method Form1::Test
不同的是第一种方式是捕获System.Exception,以及所有继承自它的类。如果你抛出了一个不是继承自System.Exception的对象,该语句就无法捕获。第二种是没有指定数据类型的catch块,称为泛化catch块,捕获的是object数据类型。这是C#2.0之前的情况。
C# 2.0中的行为稍微有别于之前版本的C#。在C# 2.0中,假如遇到用另一种语言写的代码,而且它会引发不是从System.Exception类派生的异常,那么该异常对象会被包装到一个System. Runtime.CompilerServices.RuntimeWrappedException中,后者是从System.Exception派生的。换言之,在C#程序集中,所有异常(无论它们是否从System.Exception派生)都会表现得和从System.Exception派生一样。所以在C#2.0以及以后的版本中,默认情况下上面两种方式是完全等价的。
某些语言(如 C++)允许您引发任何类型的异常。而其他语言(如 Microsoft C# 和 Visual Basic)要求每种引发的异常都派生自 Exception 类。为了保持语言间的兼容性,公共语言运行库 (CLR) 在 RuntimeWrappedException 对象中包装不是从 Exception 派生的对象。
使用RuntimeCompatibilityAttribute来指定是否包装non-cls异常。默认情况是true。
namespace System.Runtime.CompilerServices
{
// Summary:
// Specifies whether to wrap exceptions that do not derive from the System.Exception
// class with a System.Runtime.CompilerServices.RuntimeWrappedException object.
// This class cannot be inherited.
[Serializable]
[AttributeUsage(AttributeTargets.Assembly, Inherited = false, AllowMultiple = false)]
public sealed class RuntimeCompatibilityAttribute : Attribute
{
// Summary:
// Initializes a new instance of the System.Runtime.CompilerServices.RuntimeCompatibilityAttribute
// class.
public RuntimeCompatibilityAttribute();
// Summary:
// Gets or sets a value that indicates whether to wrap exceptions that do not
// derive from the System.Exception class with a System.Runtime.CompilerServices.RuntimeWrappedException
// object.
//
// Returns:
// true if exceptions that do not derive from the System.Exception class should
// appear wrapped with a System.Runtime.CompilerServices.RuntimeWrappedException
// object; otherwise, false.
public bool WrapNonExceptionThrows { get; set; }
}
}
例如在程序的AssemblyInfo.cs中加上以下代码:
[assembly: System.Runtime.CompilerServices.RuntimeCompatibility(WrapNonExceptionThrows = false)]
假如有下面的函数:
public class MyClass
{
public static void Add()
{
try
{
//do something.
}
catch (Exception)
{
}
catch
{ }
}
}
编译它,不会有任何的warning。如果把AssemblyInfo中的这一行注释掉,就会产生编译warning:
Warning 1 A previous catch clause already catches all exceptions. All non-exceptions thrown will be wrapped in a System.Runtime.CompilerServices.RuntimeWrappedException. c:\users\xiongy.corp\documents\visual studio 2010\Projects\ConsoleApplication1\ConsoleApplication1\Program.cs 32 13 ConsoleApplication1
这个warning是由最后一个无参的catch引起的,因为最后一个无参catch永远不会被执行。
避免使用异常处理来处理预料之中的情况
和大多数语言一样,在C#中引发一个异常,会对性能造成不利影响,尤其是在负责错误处理的基础结构第一次加载的时候。由于引发异常会引发性能问题,所以开发者应当尽量避免为预料之中的情况或者正常的控制流引发异常。不要用异常来验证用户输入的数据。相反,开发者应该在尝试转换之前对数据进行检查,甚至可以考虑从一开始就防止用户输入无效的数据。
Note that you should rarely catch the Exception base class, but a more specific exception class that is appropriate for the error that you anticipate. There is usually no point in catching an exception that you don't know how to handle.
2. 下面两个有什么不同:
void Test()
{
// 1.
try { }
catch (Exception ex) { throw ex; }
// 2.
try { }
catch (Exception) { throw; }
}
"throw ex" re-throws the exception object from that point. This is generally bad, since it destroys the useful call stack information which lead up to the original problem.
"throw" doesn't overwrite the stack trace from when the exception was originally thrown, so that is the correct way of rethrowing an exception.
Instead of using "throw ex" in a catch block you should throw a newly created exception containing the information that you want to add, with the original exception as the inner exception. That way you get both the original point of failure, and the point where you caught it and rethrew it.
so the correct way should be: a, you always specify the exception type. or b, you either just retrow the exception:
catch (Exception ex) {
...
throw;
}
or you throw a new exception with the original exception as an inner exception:
catch (Exception ex) {
...
throw new ApplicationException("Ooops!", ex);
}
3. using 关键字
如下的代码:
public static void Add()
{
using (FileStream fs = new FileStream("c:\\test.txt", FileMode.Open))
{
}
}
使用ildasm查看,可以发现,using最终被编译成try finally块:
.method public hidebysig static void Add() cil managed
{
// Code size 35 (0x23)
.maxstack 3
.locals init ([0] class [mscorlib]System.IO.FileStream fs,
[1] bool CS$4$0000)
IL_0000: nop
IL_0001: ldstr "c:\\test.txt"
IL_0006: ldc.i4.3
IL_0007: newobj instance void [mscorlib]System.IO.FileStream::.ctor(string,
valuetype [mscorlib]System.IO.FileMode)
IL_000c: stloc.0
.try
{
IL_000d: nop
IL_000e: nop
IL_000f: leave.s IL_0021
} // end .try
finally
{
IL_0011: ldloc.0
IL_0012: ldnull
IL_0013: ceq
IL_0015: stloc.1
IL_0016: ldloc.1
IL_0017: brtrue.s IL_0020
IL_0019: ldloc.0
IL_001a: callvirt instance void [mscorlib]System.IDisposable::Dispose()
IL_001f: nop
IL_0020: endfinally
} // end handler
IL_0021: nop
IL_0022: ret
} // end of method MyClass::Add
4. 空的try块
如下是winform的Timer类的dispose方法,用反编译工具ilspy查看:
// System.Threading.TimerBase
[SecuritySafeCritical, ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)]
public void Dispose()
{
bool flag = false;
RuntimeHelpers.PrepareConstrainedRegions();
try
{
}
finally
{
do
{
if (Interlocked.CompareExchange(ref this.m_lock, 1, 0) == 0)
{
flag = true;
try
{
this.DeleteTimerNative(null);
}
finally
{
this.m_lock = 0;
}
}
Thread.SpinWait(1);
}
while (!flag);
GC.SuppressFinalize(this);
}
}
This is to guard against Thread.Abort interrupting a process. MSDN says that:
Unexecuted finally blocks are executed before the thread is aborted.
This is because in order to recover successfully from an error, your code will need to clean up after itself. Since C# doesn't have C++-style destructors,
finally and using blocks are the only reliable way of ensuring that such cleanup is performed reliably.