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>,我碰巧实例。

50477 次浏览

在泛型类型中有一个静态字段是很好的,只要您知道每个类型参数的组合确实会得到一个字段。我猜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维基:

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

在极少数情况下,当你需要'专用的'静态字段时,

.

.

.

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

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

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

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

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

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

我想我应该添加一个例子来说明这个特性是如何有用的,例如,在这种情况下,抑制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>),因此,它们将共享_typeSpecificSerializer的实例。

thirdInstfourthInst是不同类(SerializableEntity<OtherEntity>)的实例,因此将共享_typeSpecificSerializer的实例,该实例来自其他两个类的不同的

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