Language: C# | Type: CODE_SMELL | Severity: Critical
Tags: multi-threading
This rule raises an issue each time a static field is updated from a non-static method or property.
A static field is shared by every instance of the type, so when an instance method or property writes to it, multiple objects — and
multiple threads — can read and modify the same field concurrently. This leads to unintended consequences for other instances or threads: unexpected
values, race conditions and synchronization
problems that are hard to reproduce and debug.
This rule targets unsynchronized writes to shared static state. A reported write can be intentional when the access is properly
synchronized (for example, guarded by a lock or performed through System.Threading.Interlocked), or when the type is only
ever used from a single thread. Review such cases before changing them rather than applying a blind fix.
The rule still raises on the write even when it is guarded, because the check is syntactic. In the following example the access is deliberately synchronized, so the reported issue can be left as-is:
class IdGenerator
{
private static readonly object syncRoot = new();
private static int lastId;
public int Next()
{
lock (syncRoot)
{
return ++lastId; // Noncompliant: reported, but acceptable here because the write is properly synchronized
}
}
}
Decide what the field is really for, then choose one of the following:
static as well, so it is clear the
member operates on class-level state.static field with an instance field.static and be written from instance members, guard every access so concurrent updates are safe, for example
with a lock or Interlocked.Making a member static or moving state to an instance field changes which object the data belongs to, so make sure the new scope
preserves the behavior the callers expect.
class Counter
{
private static int count = 0;
public void Increment()
{
count++; // Noncompliant: an instance method writes to the 'static' field 'count'
}
}
class Counter
{
private int count = 0; // Each Counter now tracks its own value
public void Increment()
{
count++;
}
}