Dealing with nested "using" statements in C# -
i've noticed level of nested using
statements has lately increased in code. reason because use more , more of async/await
pattern, adds @ least 1 more using
cancellationtokensource
or cancellationtokenregistration
.
so, how reduce nesting of using
, code doesn't christmas tree? similar questions have been asked on before, , i'd sum i've learnt answers.
use adjacent using
without indentation. fake example:
using (var = new filestream()) using (var b = new memorystream()) using (var c = new cancellationtokensource()) { // ... }
this may work, there's code between using
(e.g. may create object):
// ... using (var = new filestream()) { // ... using (var b = new memorystream()) { // ... using (var c = new cancellationtokensource()) { // ... } } }
combine objects of same type (or cast idisposable
) single using
, e.g.:
// ... filestream = null; memorystream b = null; cancellationtokensource c = null; // ... using (idisposable a1 = (a = new filestream()), b1 = (b = new memorystream()), c1 = (c = new cancellationtokensource())) { // ... }
this has same limitation above, plus more wordy , less readable, imo.
refactor method few methods.
this preferred way, far understand. yet, i'm curious, why following considered bad practice?
public class disposablelist : list<idisposable>, idisposable { public void dispose() { base.foreach((a) => a.dispose()); base.clear(); } } // ... using (var disposables = new disposablelist()) { var = new filestream(); disposables.add(a); // ... var b = new memorystream(); disposables.add(b); // ... var c = new cancellationtokensource(); disposables.add(c); // ... }
[update] there quite few valid points in comments nesting using
statements makes sure dispose
called on each object, if inner dispose
calls throw. however, there obscure issue: nested exceptions possibly thrown disposing of nested 'using' frames lost, besides outer one. more on here.
in single method, first option choice. in circumstances disposablelist
useful. particularly, if have many disposable fields need disposed of (in case cannot use using
). implementation given start has few problems (pointed out in comments alexei):
- requires remember add item list. (although have remember use
using
.) - aborts disposal process if 1 of dispose methods throws, leaving remaining items un-disposed.
let's fix problems:
public class disposablelist : list<idisposable>, idisposable { public void dispose() { if (this.count > 0) { list<exception> exceptions = new list<exception>(); foreach(var disposable in this) { try { disposable.dispose(); } catch (exception e) { exceptions.add(e); } } base.clear(); if (exceptions.count > 0) throw new aggregateexception(exceptions); } } public t add<t>(func<t> factory) t : idisposable { var item = factory(); base.add(item); return item; } }
now catch exceptions dispose
calls , throw new aggregateexception
after going through items. i've added helper add
method allows simpler usage:
using (var disposables = new disposablelist()) { var file = disposables.add(() => file.create("test")); // ... var memory = disposables.add(() => new memorystream()); // ... var cts = disposables.add(() => new cancellationtokensource()); // ... }
Comments
Post a Comment