IC
Size: a a a
IC
DS
IC
DS
DS
DS
DS
// SPEC: When ResourceType is a non-nullable value type, the expansion is:
// SPEC:
// SPEC: {
// SPEC: ResourceType resource = expr;
// SPEC: try { statement; }
// SPEC: finally { ((IDisposable)resource).Dispose(); }
// SPEC: }
// SPEC:
// SPEC: Otherwise, when Resource type is a nullable value type or
// SPEC: a reference type other than dynamic, the expansion is:
// SPEC:
// SPEC: {
// SPEC: ResourceType resource = expr;
// SPEC: try { statement; }
// SPEC: finally { if (resource != null) ((IDisposable)resource).Dispose(); }
// SPEC: }
// SPEC:
// SPEC: Otherwise, when ResourceType is dynamic, the expansion is:
// SPEC: {
// SPEC: dynamic resource = expr;
// SPEC: IDisposable d = (IDisposable)resource;
// SPEC: try { statement; }
// SPEC: finally { if (d != null) d.Dispose(); }
// SPEC: }
// SPEC:
// SPEC: An implementation is permitted to implement a given using statement
// SPEC: differently -- for example, for performance reasons -- as long as the
// SPEC: behavior is consistent with the above expansion.
//
// In the case of using-await statement, we'll use "IAsyncDisposable" instead of "IDisposable", "await DisposeAsync()" instead of "Dispose()"
//
// And we do in fact generate the code slightly differently than precisely how it is
// described above.
//
// First: if the type is a non-nullable value type then we do not do the
// *boxing conversion* from the resource to IDisposable. Rather, we do
// a *constrained virtual call* that elides the boxing if possible.
//
// Now, you might wonder if that is legal; isn't skipping the boxing producing
// an observable difference? Because if the value type is mutable and the Dispose
// mutates it, then skipping the boxing means that we are now mutating the original,
// not the boxed copy. But this is never observable. Either (1) we have "using(R r = x){}"
// and r is out of scope after the finally, so it is not possible to observe the mutation,
// or (2) we have "using(x) {}". But that has the semantics of "using(R temp = x){}",
// so again, we are not mutating x to begin with; we're always mutating a copy. Therefore
// it doesn't matter if we skip making *a copy of the copy*.
DS
IC
// SPEC: ResourceType resource = expr;
// SPEC: try { statement; }
// SPEC: finally { ((IDisposable)resource).Dispose(); }
IC
IC
DS
// SPEC: ResourceType resource = expr;
// SPEC: try { statement; }
// SPEC: finally { ((IDisposable)resource).Dispose(); }
IC
IC
IC
DS
IL_001a: box C/A
IL_001f: callvirt instance void [System.Private.CoreLib]System.IDisposable::Dispose()
IL_0024: nop
DS
IC
IC
AH