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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/9647641/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-09 08:18:43  来源:igfitidea点击:

ReSharper warns: "Static field in generic type"

c#asp.net-mvc-3genericsstaticresharper

提问by bevacqua

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 wrong? I would assume that this actually has a static readonlyfield for each of the possible EnumRouteConstraint<T>that I happen to instance.

这是错误的吗?我会假设这实际上static readonlyEnumRouteConstraint<T>我碰巧遇到的每个可能都有一个字段。

采纳答案by Jon Skeet

It's fine to have a static field in a generic type, so long as you know that you'll really get one field per combination of type arguments. My guess is that R# is just warning you in case you weren't aware of that.

在泛型类型中有一个静态字段是可以的,只要你知道每个类型参数组合你真的会得到一个字段。我的猜测是 R# 只是警告你,以防你不知道。

Here's an example of that:

这是一个例子:

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

As you can see, Generic<string>.Foois a different field from Generic<object>.Foo- they hold separate values.

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

回答by AakashM

From the JetBrains wiki:

来自JetBrains 维基

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 notbe 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>.Xand C<string>.Xhave completely different, independent values.

In the rare cases when you doneed 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-genericbase class to store your static members, then set your generic type to inherit from this type.

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

在极少数情况下,当您确实需要“专门的”静态字段时,请随意取消警告。

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

回答by Alexander Christov

This is not necessarily an error - it is warning you about a potentialmisunderstandingof C# generics.

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

The easiest way to remember what generics do is the following: Generics are "blueprints" for creating classes, much like classes are "blueprints" for creating objects. (Well, this is a simplification though. You may use method generics as well.)

记住泛型做什么的最简单方法如下: 泛型是创建类的“蓝图”,就像类是创建对象的“蓝图”一样。(不过,这是一种简化。您也可以使用方法泛型。)

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.

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

No wonder in VB statics are called shared. In this case however, you should be aware that such "shared" members are only shared among instances of the same exact class, and not among the distinct classes produced by substituting <T>with something else.

难怪在 VB 中静态被称为shared. 但是,在这种情况下,您应该意识到此类“共享”成员仅在同一类的实例之间共享,而不是在通过替换<T>其他内容产生的不同类之间共享。

回答by Kjartan

There are several good answers here already, that explain the warning and the reason for it. Several of these state something like having a static field in a generic type generally a mistake.

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

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.

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

Imagine you have a set of entity-classes that you want to serialize, say to Xml. You can create a serializer for this using new XmlSerializerFactory().CreateSerializer(typeof(SomeClass)), but then you will have to create a separate serializer for each type. Using generics, you can replace that with the following, which you can place in a generic class that entities can derive from:

假设您有一组要序列化的实体类,比如 Xml。您可以使用 为它创建一个序列化程序new XmlSerializerFactory().CreateSerializer(typeof(SomeClass)),但随后您必须为每种类型创建一个单独的序列化程序。使用泛型,您可以将其替换为以下内容,您可以将其放置在实体可以从中派生的泛型类中:

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

Since your probably don't want to generate a new serializer each time you need to serialize an instance of a particular type, you might add this:

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

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, then each instance of the class would use the same _typeSpecificSerializer.

如果该类不是泛型,则该类的每个实例都将使用相同的_typeSpecificSerializer.

Since it IS generic however, a set of instances with the same type for Twill share a single instance of _typeSpecificSerializer(which will have been created for that specific type), while instances with a different type for Twill use different instances of _typeSpecificSerializer.

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

An example

一个例子

Provided the two classes that extend SerializableEntity<T>:

提供了扩展的两个类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; }
}

... let's use them:

...让我们使用它们:

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

In this case, under the hood, firstInstand secondInstwill be instances of the same class (namely SerializableEntity<MyFirstEntity>), and as such, they will share an instance of _typeSpecificSerializer.

在这种情况下,在幕后,firstInstsecondInst将是同一类(即SerializableEntity<MyFirstEntity>)的实例,因此,它们将共享_typeSpecificSerializer.

thirdInstand fourthInstare instances of a different class (SerializableEntity<OtherEntity>), and so will share an instance of _typeSpecificSerializerthat is differentfrom the other two.

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

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

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