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;
}
}
¿Esto esta mal? Supongo que esto realmente tiene un static readonly
campo para cada una de las posibles EnumRouteConstraint<T>
que ocurra.
Respuestas:
Está bien tener un campo estático en un tipo genérico, siempre que sepa que realmente obtendrá un campo por combinación de argumentos de tipo. Supongo que R # solo te advierte en caso de que no lo supieras.
Aquí hay un ejemplo de eso:
Como puede ver,
Generic<string>.Foo
es un campo diferente deGeneric<object>.Foo
: contienen valores separados.fuente
class BaseFoo
contiene un miembro estático, entonces derivaré de élclass Foo<T>: BaseFoo
¿Foo<T>
compartirán todas las clases el mismo valor de miembro estático?De la wiki de JetBrains :
fuente
Esto no es necesariamente un error: le está advirtiendo sobre un posible malentendido de los genéricos de C #.
La forma más fácil de recordar lo que hacen los genéricos es la siguiente: los genéricos son "planos" para crear clases, al igual que las clases son "planos" para crear objetos. (Bueno, esta es una simplificación, sin embargo. También puede usar métodos genéricos).
Desde este punto de vista,
MyClassRecipe<T>
no es una clase, es una receta, un plano, de cómo se vería su clase. Una vez que sustituye T con algo concreto, digamos int, string, etc., obtiene una clase. Es perfectamente legal tener un miembro estático (campo, propiedad, método) declarado en su clase recién creada (como en cualquier otra clase) y no hay signos de error aquí. Sería algo sospechoso, a primera vista, si declarastatic MyStaticProperty<T> Property { get; set; }
dentro de su plan de clase, pero esto también es legal. Su propiedad también estaría parametrizada o con plantilla.No es de extrañar que en VB se llamen las estadísticas
shared
. Sin embargo, en este caso, debe tener en cuenta que dichos miembros "compartidos" solo se comparten entre instancias de la misma clase exacta, y no entre las distintas clases producidas al sustituir<T>
por otra cosa.fuente
Aquí hay varias buenas respuestas que explican la advertencia y el motivo. Varios de estos estados, como tener un campo estático en un tipo genérico, generalmente son un error .
Pensé que agregaría un ejemplo de cómo esta característica puede ser útil, es decir, un caso en el que suprimir la advertencia R # tiene sentido.
Imagine que tiene un conjunto de clases de entidad que desea serializar, digamos a Xml. Puede crear un serializador para este uso
new XmlSerializerFactory().CreateSerializer(typeof(SomeClass))
, pero luego tendrá que crear un serializador separado para cada tipo. Usando genéricos, puede reemplazar eso con lo siguiente, que puede colocar en una clase genérica de la cual las entidades pueden derivar:Dado que probablemente no desee generar un nuevo serializador cada vez que necesite serializar una instancia de un tipo en particular, puede agregar esto:
Si esta clase NO fuera genérica, entonces cada instancia de la clase usaría lo mismo
_typeSpecificSerializer
.Sin embargo, dado que ES genérico, un conjunto de instancias con el mismo tipo para
T
compartirá una única instancia de_typeSpecificSerializer
(que se habrá creado para ese tipo específico), mientras que las instancias con un tipo diferente paraT
utilizarán diferentes instancias de_typeSpecificSerializer
.Un ejemplo
Proporcionaron las dos clases que se extienden
SerializableEntity<T>
:... usémoslos:
En este caso, bajo el capó,
firstInst
ysecondInst
serán instancias de la misma clase (a saberSerializableEntity<MyFirstEntity>
), y como tal, compartirán una instancia de_typeSpecificSerializer
.thirdInst
yfourthInst
son instancias de una clase diferente (SerializableEntity<OtherEntity>
), y así se comparten una instancia de_typeSpecificSerializer
que es diferente de los otros dos.Esto significa que obtiene diferentes instancias de serializador para cada uno de sus tipos de entidad , mientras las mantiene estáticas dentro del contexto de cada tipo real (es decir, compartidas entre instancias que son de un tipo específico).
fuente