@@ -14,6 +14,7 @@ public class TestRenderer : Renderer, ITestRenderer
1414 private readonly List < RootComponent > rootComponents = new ( ) ;
1515 private readonly ILogger < TestRenderer > logger ;
1616 private readonly IRenderedComponentActivator activator ;
17+ private bool disposed ;
1718 private TaskCompletionSource < Exception > unhandledExceptionTsc = new ( TaskCreationOptions . RunContinuationsAsynchronously ) ;
1819 private Exception ? capturedUnhandledException ;
1920
@@ -81,6 +82,9 @@ public Task DispatchEventAsync(
8182 if ( fieldInfo is null )
8283 throw new ArgumentNullException ( nameof ( fieldInfo ) ) ;
8384
85+ if ( disposed )
86+ throw new ObjectDisposedException ( nameof ( TestRenderer ) ) ;
87+
8488 // Calling base.DispatchEventAsync updates the render tree
8589 // if the event contains associated data.
8690 lock ( renderTreeUpdateLock )
@@ -134,6 +138,9 @@ public IReadOnlyList<IRenderedComponentBase<TComponent>> FindComponents<TCompone
134138 /// <inheritdoc />
135139 public void DisposeComponents ( )
136140 {
141+ if ( disposed )
142+ throw new ObjectDisposedException ( nameof ( TestRenderer ) ) ;
143+
137144 // The dispatcher will always return a completed task,
138145 // when dealing with an IAsyncDisposable.
139146 // Therefore checking for a completed task and awaiting it
@@ -160,6 +167,9 @@ public void DisposeComponents()
160167 /// <inheritdoc/>
161168 protected override void ProcessPendingRender ( )
162169 {
170+ if ( disposed )
171+ return ;
172+
163173 // Blocks updates to the renderers internal render tree
164174 // while the render tree is being read elsewhere.
165175 // base.ProcessPendingRender calls UpdateDisplayAsync,
@@ -173,6 +183,9 @@ protected override void ProcessPendingRender()
173183 /// <inheritdoc/>
174184 protected override Task UpdateDisplayAsync ( in RenderBatch renderBatch )
175185 {
186+ if ( disposed )
187+ return Task . CompletedTask ;
188+
176189 if ( usersSyncContext is not null && usersSyncContext != SynchronizationContext . Current )
177190 {
178191 // The users' sync context, typically one established by
@@ -243,23 +256,34 @@ private void UpdateDisplay(in RenderBatch renderBatch)
243256 /// <inheritdoc/>
244257 protected override void Dispose ( bool disposing )
245258 {
246- if ( disposing )
259+ if ( disposed )
260+ return ;
261+
262+ disposed = true ;
263+
264+ lock ( renderTreeUpdateLock )
247265 {
248- foreach ( var rc in renderedComponents . Values )
266+ if ( disposing )
249267 {
250- rc . Dispose ( ) ;
268+ foreach ( var rc in renderedComponents . Values )
269+ {
270+ rc . Dispose ( ) ;
271+ }
272+
273+ renderedComponents . Clear ( ) ;
274+ unhandledExceptionTsc . TrySetCanceled ( ) ;
251275 }
252276
253- renderedComponents . Clear ( ) ;
254- unhandledExceptionTsc . TrySetCanceled ( ) ;
277+ base . Dispose ( disposing ) ;
255278 }
256-
257- base . Dispose ( disposing ) ;
258279 }
259280
260281 private TResult Render < TResult > ( RenderFragment renderFragment , Func < int , TResult > activator )
261282 where TResult : IRenderedFragmentBase
262283 {
284+ if ( disposed )
285+ throw new ObjectDisposedException ( nameof ( TestRenderer ) ) ;
286+
263287 var renderTask = Dispatcher . InvokeAsync ( ( ) =>
264288 {
265289 ResetUnhandledException ( ) ;
@@ -298,6 +322,9 @@ private IReadOnlyList<IRenderedComponentBase<TComponent>> FindComponents<TCompon
298322 if ( parentComponent is null )
299323 throw new ArgumentNullException ( nameof ( parentComponent ) ) ;
300324
325+ if ( disposed )
326+ throw new ObjectDisposedException ( nameof ( TestRenderer ) ) ;
327+
301328 var result = new List < IRenderedComponentBase < TComponent > > ( ) ;
302329 var framesCollection = new RenderTreeFrameDictionary ( ) ;
303330
@@ -387,7 +414,7 @@ private ArrayRange<RenderTreeFrame> GetOrLoadRenderTreeFrame(RenderTreeFrameDict
387414 /// <inheritdoc/>
388415 protected override void HandleException ( Exception exception )
389416 {
390- if ( exception is null )
417+ if ( exception is null || disposed )
391418 return ;
392419
393420 logger . LogUnhandledException ( exception ) ;
@@ -411,7 +438,7 @@ private void ResetUnhandledException()
411438
412439 private void AssertNoUnhandledExceptions ( )
413440 {
414- if ( capturedUnhandledException is Exception unhandled )
441+ if ( capturedUnhandledException is Exception unhandled && ! disposed )
415442 {
416443 capturedUnhandledException = null ;
417444
0 commit comments