ReSharper警告:“通用类型的静态字段”

时间:2012-03-10 15:33:10

标签: c# asp.net-mvc-3 generics static resharper

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

这是错的吗?我假设这实际上有一个static readonly字段用于我碰巧实例化的每个可能EnumRouteConstraint<T>

4 个答案:

答案 0 :(得分:433)

在泛型类型中有一个静态字段是很好的,只要你知道每个类型参数组合你真的会得到一个字段。我的猜测是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>.FooGeneric<object>.Foo的字段不同 - 它们包含不同的值。

答案 1 :(得分:140)

来自JetBrains wiki

  

在绝大多数情况下,在泛型类型中使用静态字段   是一个错误的迹象。原因是a中的静态字段   泛型类型将在不同关闭的实例之间共享   构造类型。这意味着对于通用类C<T>而言   有一个静态字段X,值C<int>.XC<string>.X   有完全不同的,独立的价值观。

     

在极少数情况下, 需要“专业”静态字段,   随意压制警告。

     

如果需要在实例之间共享静态字段   不同的泛型参数,定义非泛型基类   存储静态成员,然后设置要继承的泛型类型   这种类型。

答案 2 :(得分:61)

这不一定是错误 - 它会警告您有关C#泛型的潜在的 误解

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

从这个角度来看,MyClassRecipe<T>不是一个阶级 - 它是你的课程看起来像什么的食谱,蓝图。一旦用一些具体的东西替换T,比如int,string等,你就得到了一个类。在新创建的类(如在任何其他类中)声明静态成员(字段,属性,方法)并且此处没有任何错误的迹象是完全合法的。 如果您在班级蓝图中声明static MyStaticProperty<T> Property { get; set; },那么乍一看有点可疑,但这也是合法的。您的财产也将被参数化或模板化。

难怪VB静态调用shared。但是,在这种情况下,您应该知道这样的“共享”成员仅在同一个类的实例之间共享,而不是在用<T>替换其他内容而产生的不同类之间共享。

答案 3 :(得分:7)

这里有几个好的答案,解释警告及其原因。其中一些陈述类似在泛型类型中具有静态字段通常是错误

我想我会添加一个关于此功能如何有用的示例,即抑制R#警告有意义的情况。

想象一下,你有一组想要序列化的实体类,比如Xml。您可以使用new 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();

在这种情况下,firstInstsecondInst将是同一个类的实例(即SerializableEntity<MyFirstEntity>),因此,它们将共享一个{{1}的实例。 1}}。

_typeSpecificSerializerthirdInst是其他类(fourthInst)的实例,因此将共享不同的SerializableEntity<OtherEntity>实例从另外两个。

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