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

bev*_*qua 251 c# generics resharper static asp.net-mvc-3

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;
    }
}
Run Code Online (Sandbox Code Playgroud)

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

Jon*_*eet 453

在泛型类型中有一个静态字段是很好的,只要你知道每个类型参数组合你真的会得到一个字段.我的猜测是,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
    }
}
Run Code Online (Sandbox Code Playgroud)

正如您所看到的,它Generic<string>.Foo是一个不同的领域Generic<object>.Foo- 它们拥有不同的值.

  • 在这里回答我自己的评论,但是是的,如果所有 Foo&lt;T&gt; 包含在非通用基类中,它都将具有相同的静态值。见 https://dotnetfiddle.net/Wz75ya (5认同)

Aak*_*shM 143

来自JetBrains维基:

在绝大多数情况下,在泛型类型中使用静态字段是错误的标志.这样做的原因是泛型类型中的静态字段不会在不同的紧密构造类型的实例之间共享.这意味着对于C<T>具有静态字段的泛型类X,其值C<int>.XC<string>.X 具有完全不同的独立值.

在极少数情况下,当您确实需要"专用"静态字段时,请随意抑制警告.

如果需要在具有不同泛型参数的实例之间共享静态字段,请定义非泛型基类以存储静态成员,然后将通用类型设置为从此类型继承.

  • 在使用泛型类型时,从技术上讲,您最终会为您托管的每种泛型类型分别设置一个独立的类.在声明两个单独的非泛型类时,您不希望在它们之间共享静态变量,那么为什么泛型会有所不同呢?这种情况可以被认为是罕见的唯一方法是,如果大多数开发人员在创建泛型类时不理解他们正在做什么. (12认同)
  • @Syndog在泛型类中描述的静态行为看起来对我来说很好并且可以理解。但是我想这些警告背后的原因是,并非每个团队都只有经验丰富且专注的开发人员。由于开发人员的资格,正确的代码容易出错。 (2认同)

Ale*_*tov 63

这不一定是错误 - 它警告您可能存在 对C#泛型的误解.

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

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

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


Kja*_*tan 7

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

我想我会添加一个这个功能如何有用的例子,即抑制R#-warning有意义的情况.

想象一下,你有一组想要序列化的实体类,比如Xml.您可以使用此方法创建序列化程序new XmlSerializerFactory().CreateSerializer(typeof(SomeClass)),但是您必须为每种类型创建单独的序列化程序.使用泛型,您可以将其替换为以下内容,您可以将其放在实体可以派生自的泛型类中:

new XmlSerializerFactory().CreateSerializer(typeof(T))
Run Code Online (Sandbox Code Playgroud)

由于您可能不希望每次需要序列化特定类型的实例时都生成新的序列化程序,因此可以添加以下内容:

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);
     }
}
Run Code Online (Sandbox Code Playgroud)

如果这个类不是通用的,那么该类的每个实例都将使用相同的类_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; }
}
Run Code Online (Sandbox Code Playgroud)

......让我们用它们:

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();
Run Code Online (Sandbox Code Playgroud)

在这种情况下,发动机罩下,firstInstsecondInst将是相同的类(即的实例SerializableEntity<MyFirstEntity>),并且同样地,它们将共享的实例_typeSpecificSerializer.

thirdInstfourthInst是不同的类(的实例SerializableEntity<OtherEntity>)等的将共享一个实例_typeSpecificSerializer不同从其他两个.

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

  • 由于静态初始化的规则(在首次引用类之前不会调用静态初始化器),您可以放弃在 Getter 中进行检查,而只在静态实例声明中对其进行初始化。 (2认同)