.NET反编译器中using语句与try...finally的区别解析
If you compile your code in Release mode and decompile it with ildasm, you'll notice that while using is just syntactic sugar for a try...finally block, there are a few key differences in the generated IL compared to your manual implementation. Let's break them down:
Key IL Differences
1. No Null Check in the using Version
Your manual try...finally code includes an explicit if (disposable2 != null) check before calling Dispose(). In the IL, this translates to instructions like:
ldloc.1 // Load disposable2 onto the stack ldnull // Load null onto the stack ceq // Compare the two values for equality brfalse.s // Skip Dispose if disposable2 is null
The using statement, however, omits this null check entirely. The C# compiler knows the variable initialized in the using clause (var disposable1 = new Disposable()) can't be null (standard C# constructors don't return null), so it safely skips the check to optimize performance.
2. No Explicit Interface Cast in the using Version
Your manual code uses ((IDisposable)disposable2).Dispose() to explicitly cast to the interface before calling Dispose(). This adds a cast instruction to the IL:
castclass [mscorlib]System.IDisposable // Cast the object to IDisposable callvirt instance void [mscorlib]System.IDisposable::Dispose()
For the using statement, since the Disposable class directly implements IDisposable, the compiler generates a direct call to the method without the cast. The IL will look like:
callvirt instance void Disposable::Dispose()
In Release mode, the JIT may even devirtualize this call to a direct call for extra performance.
3. Tighter Local Variable Scoping
In Release mode, the JIT compiler might optimize away the disposable2 variable entirely (since it's only used in the finally block). The using statement's local variable is scoped strictly to the using block from the start, so the compiler can optimize it more efficiently without extra work.
Bottom Line
At runtime, both approaches behave nearly identically for non-null instances. But the using statement generates cleaner, more optimized IL and eliminates the risk of human error (like forgetting the null check or messing up the interface cast). It's the idiomatic, safer way to handle disposable types in C#.
内容的提问来源于stack exchange,提问作者Laurent De Cant

