Skip to main content

How to Resolve Warning "CS0728: Possibly incorrect assignment to local variable argument to using or lock" in C#

The Compiler Warning CS0728 is a logical safety warning regarding resource management. The message reads: "Possibly incorrect assignment to local variable 'variable' which is the argument to a using or lock statement. The Dispose call or unlocking will happen on the original value of the local."

In C#, the using statement (for IDisposable) and the lock statement (for threading) rely on a specific object reference to function correctly.

  • using: Must dispose of the specific object created at the start.
  • lock: Must release the lock on the specific object entered at the start.

If you reassign the variable holding that reference inside the block, you create a disconnect between the object you are using and the object the compiler intends to clean up. This leads to Resource Leaks (the original object isn't disposed) or Deadlocks (the original object isn't unlocked).

This guide explains the dangers of reassigning control variables and how to structure your code properly.

The using Statement Leak

When you write using (var x = new Resource()), the compiler generates a try/finally block. The finally block ensures that x.Dispose() is called.

If you assign a new object to x inside the block, the reference to the original object is lost. Depending on the exact compiler version and context, the finally block might dispose the new object, leaving the original object undisposed (a resource leak), or it might try to dispose the original but fail because the variable reference changed.

Example of error

Reassigning the using variable inside the block.

using System.IO;

public class FileManager
{
public void ProcessFile()
{
using (StreamReader reader = new StreamReader("file1.txt"))
{
string content = reader.ReadToEnd();

// ⛔️ Warning CS0728: Possibly incorrect assignment to local variable 'reader'.
// The original StreamReader("file1.txt") is now lost.
// It might never be Disposed properly because we overwrote the variable.
reader = new StreamReader("file2.txt");

string content2 = reader.ReadToEnd();
}
// Logic unclear: Which reader gets disposed here? The second one?
// What happened to the first one?
}
}

Solution: Use Separate Variables

Do not reuse the variable controlled by the using statement. Create a new block or a new variable for the second resource.

using System.IO;

public class FileManager
{
public void ProcessFile()
{
// ✅ Correct: First resource handled safely
using (StreamReader reader1 = new StreamReader("file1.txt"))
{
string content = reader1.ReadToEnd();
} // reader1 Disposed here

// ✅ Correct: Second resource handled safely
using (StreamReader reader2 = new StreamReader("file2.txt"))
{
string content2 = reader2.ReadToEnd();
} // reader2 Disposed here
}
}

The lock Statement Inconsistency

The lock(obj) statement ensures that Monitor.Enter(obj) is called at the start and Monitor.Exit(obj) is called at the end.

If you modify the variable obj inside the lock, you create a dangerous situation.

  1. Old Behavior: The code might try to unlock the new object (which wasn't locked), throwing an exception, while leaving the old object locked forever (Deadlock).
  2. Modern Behavior: The compiler might cache the original object reference to ensure Exit works correctly, meaning your assignment to obj is effectively ignored by the locking mechanism, leading to confusion about which object is actually protecting the code.

Example of error

Assigning a new value to the lock object.

public class ThreadSafeCounter
{
private object _syncRoot = new object();

public void DoWork()
{
// We lock on the object currently in _syncRoot (Object A)
lock (_syncRoot)
{
// ... critical section ...

// ⛔️ Warning CS0728: Possibly incorrect assignment.
// We change the variable to point to Object B.
// Ideally, we should unlock Object A when we leave.
// But modifying the variable makes the logic ambiguous and dangerous.
_syncRoot = new object();
}
}
}

Solution: Use readonly Lock Objects

The object used for locking should never change. Mark the field as readonly to prevent accidental reassignment entirely.

public class ThreadSafeCounter
{
// ✅ Correct: 'readonly' prevents reassignment.
// The compiler knows this reference will never change.
private readonly object _syncRoot = new object();

public void DoWork()
{
lock (_syncRoot)
{
// Safe critical section.
// We physically cannot reassign _syncRoot here.
}
}
}
note

Best Practice: Always define a dedicated private readonly object _lock = new object(); specifically for locking. Never lock on variables that hold data (like strings or collections) that might be replaced.

Conclusion

CS0728 warns you that you are sabotaging the cleanup mechanism of your code.

  1. For using: Treat the variable defined in the using statement as read-only. If you need a different resource, start a new using block.
  2. For lock: Always lock on readonly objects. Changing the lock object while inside the lock is a recipe for deadlocks and threading crashes.