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

这是错的吗?我假设这实际上有一个静态只读字段为每个可能的枚举routeconstraint <T>,我碰巧实例。


在泛型类型中有一个静态字段是很好的,只要您知道每个类型参数的组合确实会得到一个字段。我猜r#只是警告你,以防你没有意识到这一点。

这里有一个例子:

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

如您所见,Generic<string>。Foo是一个不同于Generic<object>的字段。Foo -它们持有不同的值。


来自JetBrains wiki:

In the vast majority of cases, having a static field in a generic type is a sign of an error. The reason for this is that a static field in a generic type will not be shared among instances of different close constructed types. This means that for a generic class C<T> which has a static field X, the values of C<int>.X and C<string>.X have completely different, independent values. In the rare cases when you do need the 'specialized' static fields, feel free to suppress the warning. If you need to have a static field shared between instances with different generic arguments, define a non-generic base class to store your static members, then set your generic type to inherit from this type.


这并不一定是一个错误——这是在警告你对c#泛型的潜在误解。

记住泛型的作用最简单的方法如下: 泛型是创建类的“蓝图”,就像类是创建对象的“蓝图”一样。(好吧,这是一种简化。你也可以使用方法泛型。)

From this point of view MyClassRecipe<T> is not a class -- it is a recipe, a blueprint, of what your class would look like. Once you substitute T with something concrete, say int, string, etc., you get a class. It is perfectly legal to have a static member (field, property, method) declared in your newly created class (as in any other class) and no sign of any error here. It would be somewhat suspicious, at first sight, if you declare static MyStaticProperty<T> Property { get; set; } within your class blueprint, but this is legal too. Your property would be parameterized, or templated, as well.

难怪在VB中静态被称为共享。但是,在这种情况下,您应该意识到这种“共享”成员只在同一个类的实例之间共享,而不是在用其他东西替换<T>所产生的不同类之间共享。


这里已经有几个很好的答案,解释了这个警告及其原因。其中有几个表示在泛型类型中拥有静态字段通常是错误的。

我想我应该添加一个例子来说明这个特性是如何有用的,例如,在这种情况下,抑制r#警告是有意义的。

假设您有一组想要序列化的实体类,比如序列化为Xml。您可以使用新的XmlSerializerFactory(). createserializer (typeof(SomeClass))为此创建一个序列化器,但随后必须为每种类型创建一个单独的序列化器。使用泛型,你可以用下面的替换,你可以把它放在实体可以派生的泛型类中:

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

因为你可能不想每次需要序列化一个特定类型的实例时都生成一个新的序列化器,你可以添加这个:

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

如果这个类不是泛型的,那么该类的每个实例将使用相同的_typeSpecificSerializer。

然而,由于它是泛型的,具有相同类型T的一组实例将共享单个_typeSpecificSerializer实例(该实例将为该特定类型创建),而具有不同类型T的实例将使用不同的_typeSpecificSerializer实例。

一个例子

提供了两个扩展SerializableEntity<T>的类:

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

... 让我们使用它们:

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();

在本例中,在底层,firstst和secondInst将是同一个类的实例(即SerializableEntity<MyFirstEntity>),因此,它们将共享_typeSpecificSerializer的实例。

thirdInst和fourthInst是不同类的实例(SerializableEntity<OtherEntity>),因此将共享与其他两个类不同的_typeSpecificSerializer实例。

这意味着您可以为每个实体类型获得不同的序列化器实例,同时在每个实际类型的上下文中仍然保持它们的静态(即,在特定类型的实例之间共享)。