Coder Perfect

“Static field in generic,” cautions ReSharper.

Problem

public class EnumRouteConstraint<T> : IRouteConstraint
    where T : struct
{
    private static readonly Lazy<HashSet<string>> _enumNames; // <--

    static EnumRouteConstraint()
    {
        if (!typeof(T).IsEnum)
        {
            throw new ArgumentException(
                Resources.Error.EnumRouteConstraint.FormatWith(typeof(T).FullName));
        }

        string[] names = Enum.GetNames(typeof(T));
        _enumNames = new Lazy<HashSet<string>>(() => new HashSet<string>
        (
            names.Select(name => name), StringComparer.InvariantCultureIgnoreCase
        ));
    }

    public bool Match(HttpContextBase httpContext, Route route, 
                        string parameterName, RouteValueDictionary values, 
                        RouteDirection routeDirection)
    {
        bool match = _enumNames.Value.Contains(values[parameterName].ToString());
        return match;
    }
}

Is this incorrect? I would assume that this actually has a static readonly field for each of the possible EnumRouteConstraint that I happen to instance.

Asked by bevacqua

Solution #1

It’s OK to have a static field in a generic type if you know you’ll only get one field per type argument combination. R# is probably simply letting you know in case you weren’t aware.

As an example, consider the following:

using System;

public class Generic<T>
{
    // Of course we wouldn't normally have public fields, but...
    public static int Foo;
}

public class Test
{
    public static void Main()
    {
        Generic<string>.Foo = 20;
        Generic<object>.Foo = 10;
        Console.WriteLine(Generic<string>.Foo); // 20
    }
}

Genericstring>, as you can see. Genericobject> and Foo are two separate fields. They each have their own set of values, Foo.

Answered by Jon Skeet

Solution #2

According to the JetBrains wiki:

Answered by AakashM

Solution #3

This isn’t always an error; rather, it’s a caution that you might be misusing C# generics.

The following is the simplest method to remember what generics do: Generics are “blueprints” for constructing classes, much as classes are for constructing objects. (Of course, this is a simplification.) You may also utilize method generics.)

MyClassRecipeT> is not a class in this sense; it’s a recipe, a blueprint, for how your class should look. You get a class when you replace T with something concrete, like int, string, or whatever. It is absolutely permissible to define a static member (field, property, or method) in your freshly constructed class (like in any other class), and there is no indication of any issue. Although declaring static MyStaticPropertyT> Property get; set; within your class blueprint may appear suspicious at first glance, it is allowed. In addition, your property would be parameterized or templated.

It’s no surprise that statics in VB are referred to as shared. However, you should be mindful that such “shared” members are only shared between instances of the same exact class, not between the unique classes created by replacing T> with something else.

Answered by Alexander Christov

Solution #4

There are already a few decent replies here that explain the warning and why it exists. Having a static field in a generic type is often a mistake, according to several of them.

I thought I’d add an example of how this feature can be useful, i.e. a case where suppressing the R#-warning makes sense.

Consider the following scenario: you have a set of entity-classes that you wish to serialize to Xml. Using the new XmlSerializerFactory, you can create a serializer for this (). CreateSerializer(typeof(SomeClass)), but you’ll have to make a serializer for each type separately. You can replace it with the following, which you may put in a generic class from which entities can derive, using generics:

new XmlSerializerFactory().CreateSerializer(typeof(T))

Because you presumably don’t want to create a new serializer every time you need to serialize an instance of a specific type, you may include the following:

public class SerializableEntity<T>
{
    // ReSharper disable once StaticMemberInGenericType
    private static XmlSerializer _typeSpecificSerializer;

    private static XmlSerializer TypeSpecificSerializer
    {
        get
        {
            // Only create an instance the first time. In practice, 
            // that will mean once for each variation of T that is used,
            // as each will cause a new class to be created.
            if ((_typeSpecificSerializer == null))
            {
                _typeSpecificSerializer = 
                    new XmlSerializerFactory().CreateSerializer(typeof(T));
            }

            return _typeSpecificSerializer;
        }
    }

    public virtual string Serialize()
    {
        // .... prepare for serializing...

        // Access _typeSpecificSerializer via the property, 
        // and call the Serialize method, which depends on 
        // the specific type T of "this":
        TypeSpecificSerializer.Serialize(xmlWriter, this);
     }
}

If this class was not generic, it would utilize the same _typeSpecificSerializer for each instance.

However, because it is generic, instances with the same type for T will share a single instance of _typeSpecificSerializer (which will have been constructed for that specific type), while examples with various types for T will use different instances of _typeSpecificSerializer.

The following two classes extend SerializableEntityT>:

// Note that T is MyFirstEntity
public class MyFirstEntity : SerializableEntity<MyFirstEntity>
{
    public string SomeValue { get; set; }
}

// Note that T is OtherEntity
public class OtherEntity : SerializableEntity<OtherEntity >
{
    public int OtherValue { get; set; }
}

Let’s put them to work:

var firstInst = new MyFirstEntity{ SomeValue = "Foo" };
var secondInst = new MyFirstEntity{ SomeValue = "Bar" };

var thirdInst = new OtherEntity { OtherValue = 123 };
var fourthInst = new OtherEntity { OtherValue = 456 };

var xmlData1 = firstInst.Serialize();
var xmlData2 = secondInst.Serialize();
var xmlData3 = thirdInst.Serialize();
var xmlData4 = fourthInst.Serialize();

Under the hood, firstInst and secondInst will be instances of the same class (specifically SerializableEntityMyFirstEntity>), and as a result, they will share a _typeSpecificSerializer instance.

Because thirdInst and fourthInst are instances of a distinct class (SerializableEntityOtherEntity>), they will share a unique instance of _typeSpecificSerializer.

This implies you’ll get different serializer-instances for each of your entity types while keeping them static within each type’s context (i.e., shared among instances that are of a specific type).

Answered by Kjartan

Post is based on https://stackoverflow.com/questions/9647641/resharper-warns-static-field-in-generic-type