Skip to content

Commit bddb1b0

Browse files
committed
Correctly implement the IDispose pattern
1 parent 5779b5e commit bddb1b0

File tree

10 files changed

+194
-46
lines changed

10 files changed

+194
-46
lines changed

RetailCoder.VBE/API/ParserState.cs

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -133,9 +133,24 @@ public Declaration[] UserDeclarations
133133
get { return _userDeclarations; }
134134
}
135135

136+
private bool _dispose = true;
136137
public void Dispose()
137138
{
138-
_state.StateChanged -= _state_StateChanged;
139+
Dispose(_dispose);
140+
_dispose = false;
141+
}
142+
143+
protected virtual void Dispose(bool disposing)
144+
{
145+
if (!disposing)
146+
{
147+
return;
148+
}
149+
150+
if (_state != null)
151+
{
152+
_state.StateChanged -= _state_StateChanged;
153+
}
139154
}
140155
}
141156
}

RetailCoder.VBE/App.cs

Lines changed: 58 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -26,12 +26,12 @@ public class App : IDisposable
2626
private readonly VBE _vbe;
2727
private readonly IMessageBox _messageBox;
2828
private readonly IRubberduckParser _parser;
29-
private readonly AutoSave.AutoSave _autoSave;
30-
private readonly IGeneralConfigService _configService;
31-
private readonly IAppMenu _appMenus;
32-
private readonly RubberduckCommandBar _stateBar;
29+
private AutoSave.AutoSave _autoSave;
30+
private IGeneralConfigService _configService;
31+
private IAppMenu _appMenus;
32+
private RubberduckCommandBar _stateBar;
3333
private readonly IIndenter _indenter;
34-
private readonly IRubberduckHooks _hooks;
34+
private IRubberduckHooks _hooks;
3535

3636
private readonly Logger _logger;
3737

@@ -387,19 +387,56 @@ private void LoadConfig()
387387
}
388388
}
389389

390+
private bool _dispose = true;
390391
public void Dispose()
391392
{
392-
_hooks.MessageReceived -= _hooks_MessageReceived;
393-
_configService.SettingsChanged -= _configService_SettingsChanged;
394-
_configService.LanguageChanged -= ConfigServiceLanguageChanged;
395-
_parser.State.StateChanged -= Parser_StateChanged;
396-
_parser.State.StatusMessageUpdate -= State_StatusMessageUpdate;
397-
_stateBar.Refresh -= _stateBar_Refresh;
398-
399-
_sink.ProjectAdded -= sink_ProjectAdded;
400-
_sink.ProjectRemoved -= sink_ProjectRemoved;
401-
_sink.ProjectActivated -= sink_ProjectActivated;
402-
_sink.ProjectRenamed -= sink_ProjectRenamed;
393+
Dispose(_dispose);
394+
_dispose = false;
395+
}
396+
397+
protected virtual void Dispose(bool disposing)
398+
{
399+
if (!disposing)
400+
{
401+
return;
402+
}
403+
404+
if (_hooks != null)
405+
{
406+
_hooks.MessageReceived -= _hooks_MessageReceived;
407+
_hooks.Dispose();
408+
_hooks = null;
409+
}
410+
411+
if (_configService != null)
412+
{
413+
_configService.SettingsChanged -= _configService_SettingsChanged;
414+
_configService.LanguageChanged -= ConfigServiceLanguageChanged;
415+
_configService = null;
416+
}
417+
418+
if (_parser != null && _parser.State != null)
419+
{
420+
_parser.State.StateChanged -= Parser_StateChanged;
421+
_parser.State.StatusMessageUpdate -= State_StatusMessageUpdate;
422+
// I won't set this to null because other components may try to release things
423+
}
424+
425+
if (_stateBar != null)
426+
{
427+
_stateBar.Refresh -= _stateBar_Refresh;
428+
_stateBar.Dispose();
429+
_stateBar = null;
430+
}
431+
432+
if (_sink != null)
433+
{
434+
_sink.ProjectAdded -= sink_ProjectAdded;
435+
_sink.ProjectRemoved -= sink_ProjectRemoved;
436+
_sink.ProjectActivated -= sink_ProjectActivated;
437+
_sink.ProjectRenamed -= sink_ProjectRenamed;
438+
_sink = null;
439+
}
403440

404441
foreach (var item in _componentsEventsSinks)
405442
{
@@ -411,7 +448,11 @@ public void Dispose()
411448
item.Value.ComponentSelected -= sink_ComponentSelected;
412449
}
413450

414-
_autoSave.Dispose();
451+
if (_autoSave != null)
452+
{
453+
_autoSave.Dispose();
454+
_autoSave = null;
455+
}
415456

416457
_projectsEventsConnectionPoint.Unadvise(_projectsEventsCookie);
417458
foreach (var item in _componentsEventsConnectionPoints)
@@ -422,9 +463,6 @@ public void Dispose()
422463
{
423464
item.Value.Item1.Unadvise(item.Value.Item2);
424465
}
425-
_hooks.Dispose();
426-
427-
_stateBar.Dispose();
428466
}
429467
}
430468
}

RetailCoder.VBE/AutoSave/AutoSave.cs

Lines changed: 27 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,7 @@ public class AutoSave : IDisposable
1111
{
1212
private readonly VBE _vbe;
1313
private readonly IGeneralConfigService _configService;
14-
private readonly Timer _timer = new Timer();
15-
private Configuration _config;
14+
private Timer _timer = new Timer();
1615

1716
private const int VbeSaveCommandId = 3;
1817

@@ -29,12 +28,12 @@ public AutoSave(VBE vbe, IGeneralConfigService configService)
2928

3029
private void ConfigServiceSettingsChanged(object sender, EventArgs e)
3130
{
32-
_config = _configService.LoadConfiguration();
31+
var config = _configService.LoadConfiguration();
3332

34-
_timer.Enabled = _config.UserSettings.GeneralSettings.AutoSaveEnabled
35-
&& _config.UserSettings.GeneralSettings.AutoSavePeriod != 0;
33+
_timer.Enabled = config.UserSettings.GeneralSettings.AutoSaveEnabled
34+
&& config.UserSettings.GeneralSettings.AutoSavePeriod != 0;
3635

37-
_timer.Interval = _config.UserSettings.GeneralSettings.AutoSavePeriod * 1000;
36+
_timer.Interval = config.UserSettings.GeneralSettings.AutoSavePeriod * 1000;
3837
}
3938

4039
private void _timer_Elapsed(object sender, ElapsedEventArgs e)
@@ -55,12 +54,31 @@ private void _timer_Elapsed(object sender, ElapsedEventArgs e)
5554
}
5655
}
5756

57+
private bool _dispose = true;
5858
public void Dispose()
5959
{
60-
_configService.LanguageChanged -= ConfigServiceSettingsChanged;
61-
_timer.Elapsed -= _timer_Elapsed;
60+
Dispose(_dispose);
61+
_dispose = false;
62+
}
63+
64+
protected virtual void Dispose(bool disposing)
65+
{
66+
if (!disposing)
67+
{
68+
return;
69+
}
6270

63-
_timer.Dispose();
71+
if (_configService != null)
72+
{
73+
_configService.LanguageChanged -= ConfigServiceSettingsChanged;
74+
}
75+
76+
if (_timer != null)
77+
{
78+
_timer.Elapsed -= _timer_Elapsed;
79+
_timer.Dispose();
80+
_timer = null;
81+
}
6482
}
6583
}
6684
}

RetailCoder.VBE/Extension.cs

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ public class _Extension : IDTExtensibility2
2323
private const string ClassId = "8D052AD8-BBD2-4C59-8DEC-F697CA1F8A66";
2424
private const string ProgId = "Rubberduck.Extension";
2525

26-
private readonly IKernel _kernel = new StandardKernel(new FuncModule());
26+
private IKernel _kernel;
2727
private App _app;
2828

2929
public void OnAddInsUpdate(ref Array custom)
@@ -37,6 +37,8 @@ public void OnBeginShutdown(ref Array custom)
3737
// ReSharper disable InconsistentNaming
3838
public void OnConnection(object Application, ext_ConnectMode ConnectMode, object AddInInst, ref Array custom)
3939
{
40+
_kernel = new StandardKernel(new FuncModule());
41+
4042
try
4143
{
4244
var currentDomain = AppDomain.CurrentDomain;
@@ -69,13 +71,17 @@ public void OnStartupComplete(ref Array custom)
6971

7072
public void OnDisconnection(ext_DisconnectMode RemoveMode, ref Array custom)
7173
{
72-
var addin = _kernel.Get<AddIn>();
73-
74-
_app.Shutdown();
75-
_kernel.Dispose();
74+
if (_app != null)
75+
{
76+
_app.Shutdown();
77+
_app = null;
78+
}
7679

77-
Marshal.FinalReleaseComObject(addin);
78-
GC.SuppressFinalize(addin);
80+
if (_kernel != null)
81+
{
82+
_kernel.Dispose();
83+
_kernel = null;
84+
}
7985
}
8086
}
8187
}

RetailCoder.VBE/Inspections/Inspector.cs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -132,9 +132,11 @@ before moving them into the ParseTreeResults after qualifying them
132132
return result;
133133
}
134134

135+
private bool _dispose = true;
135136
public void Dispose()
136137
{
137-
Dispose(true);
138+
Dispose(_dispose);
139+
_dispose = false;
138140
}
139141

140142
protected virtual void Dispose(bool disposing)

RetailCoder.VBE/Navigation/CodeExplorer/CodeExplorerViewModel.cs

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -361,10 +361,25 @@ private void ExecuteRemoveComand(object param)
361361
_externalRemoveCommand.Execute(param);
362362
}
363363

364+
private bool _dispose = true;
364365
public void Dispose()
365366
{
366-
_state.StateChanged -= ParserState_StateChanged;
367-
_state.ModuleStateChanged -= ParserState_ModuleStateChanged;
367+
Dispose(_dispose);
368+
_dispose = false;
369+
}
370+
371+
protected virtual void Dispose(bool disposing)
372+
{
373+
if (!disposing)
374+
{
375+
return;
376+
}
377+
378+
if (_state != null)
379+
{
380+
_state.StateChanged -= ParserState_StateChanged;
381+
_state.ModuleStateChanged -= ParserState_ModuleStateChanged;
382+
}
368383
}
369384
}
370385
}

RetailCoder.VBE/UI/CodeInspections/InspectionResultsViewModel.cs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -342,8 +342,20 @@ private bool CanExecuteCopyResultsCommand(object parameter)
342342
return !IsBusy && _results != null && _results.Any();
343343
}
344344

345+
private bool _dispose = true;
345346
public void Dispose()
346347
{
348+
Dispose(_dispose);
349+
_dispose = false;
350+
}
351+
352+
protected virtual void Dispose(bool disposing)
353+
{
354+
if (!disposing)
355+
{
356+
return;
357+
}
358+
347359
_state.StateChanged -= _state_StateChanged;
348360
}
349361
}

RetailCoder.VBE/UI/Controls/SearchResultPresenterInstanceManager.cs

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -39,18 +39,30 @@ private void viewModel_LastTabClosed(object sender, EventArgs e)
3939
{
4040
_presenter.Hide();
4141
}
42-
42+
43+
private bool _dispose = true;
4344
public void Dispose()
4445
{
46+
Dispose(_dispose);
47+
_dispose = false;
48+
}
49+
50+
protected virtual void Dispose(bool disposing)
51+
{
52+
if (!disposing)
53+
{
54+
return;
55+
}
56+
4557
if (_view.ViewModel != null)
4658
{
47-
_view.ViewModel.LastTabClosed -= viewModel_LastTabClosed;
59+
_view.ViewModel.LastTabClosed -= viewModel_LastTabClosed;
4860
}
4961

5062
if (_presenter != null)
5163
{
52-
_presenter.Dispose();
64+
_presenter.Dispose();
65+
}
5366
}
5467
}
55-
}
5668
}

RetailCoder.VBE/UI/SourceControl/SourceControlViewViewModel.cs

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -560,9 +560,24 @@ public ICommand LoginGridCancelCommand
560560
}
561561
}
562562

563+
private bool _dispose = true;
563564
public void Dispose()
564565
{
565-
_state.StateChanged -= _state_StateChanged;
566+
Dispose(_dispose);
567+
_dispose = false;
568+
}
569+
570+
protected virtual void Dispose(bool disposing)
571+
{
572+
if (!disposing)
573+
{
574+
return;
575+
}
576+
577+
if (_state != null)
578+
{
579+
_state.StateChanged -= _state_StateChanged;
580+
}
566581
}
567582
}
568583
}

RetailCoder.VBE/UI/ToDoItems/ToDoExplorerViewModel.cs

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -148,9 +148,24 @@ private IEnumerable<ToDoItem> GetItems()
148148
return _state.AllComments.SelectMany(GetToDoMarkers);
149149
}
150150

151+
private bool _dispose = true;
151152
public void Dispose()
152153
{
153-
_state.StateChanged -= _state_StateChanged;
154+
Dispose(_dispose);
155+
_dispose = false;
156+
}
157+
158+
protected virtual void Dispose(bool disposing)
159+
{
160+
if (!disposing)
161+
{
162+
return;
163+
}
164+
165+
if (_state != null)
166+
{
167+
_state.StateChanged -= _state_StateChanged;
168+
}
154169
}
155170
}
156171
}

0 commit comments

Comments
 (0)