← back to index

S2696 — Instance members should not write to "static" fields

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.

Why is this an issue?

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.

Exceptions

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
    }
  }
}

How to fix it

Decide what the field is really for, then choose one of the following:

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.

Code examples

Noncompliant code example

class Counter
{
  private static int count = 0;

  public void Increment()
  {
    count++;  // Noncompliant: an instance method writes to the 'static' field 'count'
  }
}

Compliant solution

class Counter
{
  private int count = 0; // Each Counter now tracks its own value

  public void Increment()
  {
    count++;
  }
}

Resources

Documentation

Articles & blog posts

Standards