Open sandboxFocusImprove this doc

THR040: Access Level Attribute Applied to a Static Method

Severity Warning
Reported by PostSharp.Patterns.Threading
Message The [<ThreadingModel>] threading model ignores the access level attribute of "<Method>" because it is a static method. Access level attributes, such as [Reader] and [Writer], apply only to instance methods.

Cause

The access level attributes, ReaderAttribute, WriterAttribute, UpgradeableReaderAttribute and YielderAttribute, specify the lock that a method acquires on the object, for example under the ReaderWriterSynchronizedAttribute threading model. The lock belongs to an instance, so a static method, which has no instance, cannot acquire it.

When the threading model of a class analyzes its members, it reports THR040 for each static method of the class that has an access level attribute, and ignores the attribute. <ThreadingModel> is the name of the threading model aspect of the class, for example ReaderWriterSynchronizedAttribute.

In the following example, the static FormatPrice method of a reader-writer-synchronized class is annotated with [Reader].

using System;
using PostSharp.Patterns.Threading;

namespace Errors.THR040_Error;

[ReaderWriterSynchronized]
public class ProductCatalog
{
    private readonly decimal penPrice = 1.5m;

    [Reader]
    public decimal GetPenPrice() => this.penPrice;

    // A static method does not access the state of an instance, so it cannot take its lock.
    [Reader]
    public static string FormatPrice( decimal price ) => $"{price:0.00} EUR";
}

public static class Program
{
    public static void Main()
    {
        var catalog = new ProductCatalog();
        Console.WriteLine( ProductCatalog.FormatPrice( catalog.GetPenPrice() ) );
    }
}
warning THR040: THR040_Error.cs(16,26): The [ReaderWriterSynchronizedAttribute] threading model ignores the access level attribute of "Errors.THR040_Error.ProductCatalog.FormatPrice(System.Decimal)" because it is a static method. Access level attributes, such as [Reader] and [Writer], apply only to instance methods.

Resolution

  • When the static method does not access the state of an instance, as FormatPrice does not, remove the attribute.
  • When the method reads or writes the fields of an instance, make it an instance method, so that the attribute acquires the lock of that instance.
  • When the method accesses static state, protect it with another mechanism: the threading models do not synchronize static fields.

In the following example, the attribute is removed from the static method. The program behaves as before, because the threading model ignored the attribute.

using System;
using PostSharp.Patterns.Threading;

namespace Errors.THR040_Fixed;

[ReaderWriterSynchronized]
public class ProductCatalog
{
    private readonly decimal penPrice = 1.5m;

    [Reader]
    public decimal GetPenPrice() => this.penPrice;

    // The static method has no access level attribute.
    public static string FormatPrice( decimal price ) => $"{price:0.00} EUR";
}

public static class Program
{
    public static void Main()
    {
        var catalog = new ProductCatalog();
        Console.WriteLine( ProductCatalog.FormatPrice( catalog.GetPenPrice() ) );
    }
}
1.50 EUR

See Also

Reference

AccessLevelAttribute
ReaderWriterSynchronizedAttribute

Other Resources

Reader/Writer Synchronized Threading Model