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;
}
}
Est-ce mal? Je suppose que cela a en fait un static readonly
champ pour chacun des possibles EnumRouteConstraint<T>
que j'arrive à l'instance.
Réponses:
C'est bien d'avoir un champ statique dans un type générique, tant que vous savez que vous obtiendrez vraiment un champ par combinaison d'arguments de type. Je suppose que R # vous avertit juste au cas où vous ne seriez pas au courant de cela.
Voici un exemple de cela:
Comme vous pouvez le voir,
Generic<string>.Foo
est un champ différent deGeneric<object>.Foo
- ils contiennent des valeurs distinctes.la source
class BaseFoo
contenant un membre statique, puis en dériverai-class Foo<T>: BaseFoo
je toutes lesFoo<T>
classes partageront-elles la même valeur de membre statique?Du wiki JetBrains :
la source
Ce n'est pas nécessairement une erreur - cela vous avertit d'un malentendu potentiel des génériques C #.
La façon la plus simple de se souvenir de ce que font les génériques est la suivante: Les génériques sont des "plans" pour créer des classes, tout comme les classes sont des "plans" pour créer des objets. (Eh bien, c'est une simplification cependant. Vous pouvez également utiliser des génériques de méthode.)
De ce point de vue, ce
MyClassRecipe<T>
n'est pas une classe - c'est une recette, un plan directeur, à quoi ressemblerait votre classe. Une fois que vous remplacez T par quelque chose de concret, disons int, string, etc., vous obtenez une classe. Il est parfaitement légal d'avoir un membre statique (champ, propriété, méthode) déclaré dans votre classe nouvellement créée (comme dans toute autre classe) et aucun signe d'erreur ici. Ce serait un peu suspect, à première vue, si vous déclarezstatic MyStaticProperty<T> Property { get; set; }
dans votre plan de classe, mais c'est légal aussi. Votre propriété serait également paramétrée ou modélisée.Pas étonnant que la statique VB soit appelée
shared
. Dans ce cas cependant, vous devez être conscient que ces membres "partagés" ne sont partagés qu'entre des instances de la même classe exacte, et non entre les classes distinctes produites en les remplaçant<T>
par autre chose.la source
Il y a déjà plusieurs bonnes réponses ici, qui expliquent l'avertissement et la raison de celui-ci. Plusieurs de ceux-ci indiquent quelque chose comme avoir un champ statique dans un type générique généralement une erreur .
J'ai pensé ajouter un exemple de la façon dont cette fonctionnalité peut être utile, c'est-à-dire un cas où la suppression de l'avertissement R # est logique.
Imaginez que vous ayez un ensemble de classes d'entité que vous souhaitez sérialiser, par exemple en Xml. Vous pouvez créer un sérialiseur pour cela en utilisant
new XmlSerializerFactory().CreateSerializer(typeof(SomeClass))
, mais vous devrez ensuite créer un sérialiseur distinct pour chaque type. En utilisant des génériques, vous pouvez remplacer cela par ce qui suit, que vous pouvez placer dans une classe générique dont les entités peuvent dériver:Étant donné que vous ne souhaitez probablement pas générer un nouveau sérialiseur chaque fois que vous devez sérialiser une instance d'un type particulier, vous pouvez ajouter ceci:
Si cette classe n'était PAS générique, alors chaque instance de la classe utiliserait la même
_typeSpecificSerializer
.Puisqu'il est générique cependant, un ensemble d'instances avec le même type pour
T
partagera une seule instance de_typeSpecificSerializer
(qui aura été créée pour ce type spécifique), tandis que les instances avec un type différent pourT
utiliseront différentes instances de_typeSpecificSerializer
.Un exemple
A condition que les deux classes qui s'étendent
SerializableEntity<T>
:... utilisons-les:
Dans ce cas, sous le capot,
firstInst
etsecondInst
seront des instances de la même classe (à savoirSerializableEntity<MyFirstEntity>
), et en tant que tels, ils partageront une instance de_typeSpecificSerializer
.thirdInst
etfourthInst
sont des instances d'une classe différente (SerializableEntity<OtherEntity>
), et partageront donc une instance_typeSpecificSerializer
qui est différente des deux autres.Cela signifie que vous obtenez différentes instances de sérialiseur pour chacun de vos types d' entités , tout en les gardant statiques dans le contexte de chaque type réel (c'est-à-dire partagées entre les instances d'un type spécifique).
la source