JetBrains Rider 2024.1 Help

Code inspection: Access to disposed captured variable

First of all, let's make sure that you understand what a closure is. To put it simply, a closure in C# is a lambda expression or an anonymous method that captures some variables from an outer scope. Here is the simplest example:

// A self-contained lambda. Not a closure. Action printOne = () => { Console.WriteLine("one"); }; // A closure – a lambda that captures a variable from an outer scope. string myStr = "one"; Action print = () => { Console.WriteLine(myStr); };

In the example above, print will capture the variable myStr (and not its value) and will only get the value of myStr when you invoke print().

In more complex scenarios, when a closure is defined in a changing context, it may not behave as expected.

One of the situations where it may happen is a closure defined inside a using statement:

void Foo(string fileName, string text) { using (var writer = new StreamWriter(fileName)) { ExecuteDelayed(() => { writer.Write(text); }); } } private static void ExecuteDelayed(Action action) { // execute action }

In the above code, JetBrains Rider issues the Access to disposed closure warning for writer.Write(text);. The reason for that is ExecuteDelayed() could execute the lambda after the writer's scope is disposed, which will result in a runtime exception.

If the ExecuteDelayed() completes processing of the lambda on the stack, you can mark the action parameter with the InstantHandle attribute:

private static void ExecuteDelayed([InstantHandle]Action action) { // execute action }

This will tell JetBrains Rider's code analysis engine that using ExecuteDelayed() is safe in such contexts, and no warnings will be issued.

Last modified: 11 February 2024