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;
}
}
Isso está errado? Eu diria que isso realmente tem um static readonly
campo para cada um dos possíveis EnumRouteConstraint<T>
que eu ocorra.
Respostas:
Não há problema em ter um campo estático em um tipo genérico, desde que você saiba que realmente obterá um campo por combinação de argumentos de tipo. Meu palpite é que o R # está apenas avisando caso você não esteja ciente disso.
Aqui está um exemplo disso:
Como você pode ver,
Generic<string>.Foo
é um campo diferente deGeneric<object>.Foo
- eles mantêm valores separados.fonte
class BaseFoo
contendo um membro estático, daí derivarclass Foo<T>: BaseFoo
todas asFoo<T>
classes compartilharão o mesmo valor de membro estático?No wiki do JetBrains :
fonte
Isso não é necessariamente um erro - está avisando sobre um possível mal-entendido dos genéricos de C #.
A maneira mais fácil de lembrar o que os genéricos fazem é o seguinte: Genéricos são "blueprints" para criar classes, assim como as classes são "blueprints" para criar objetos. (Bem, isso é uma simplificação. Você também pode usar genéricos de métodos.)
Desse ponto de vista,
MyClassRecipe<T>
não é uma classe - é uma receita, um plano, da aparência da sua classe. Depois de substituir T por algo concreto, por exemplo, int, string etc., você obtém uma classe. É perfeitamente legal ter um membro estático (campo, propriedade, método) declarado em sua classe recém-criada (como em qualquer outra classe) e nenhum sinal de erro aqui. Seria um pouco suspeito, à primeira vista, se você declararstatic MyStaticProperty<T> Property { get; set; }
dentro do plano da sua classe, mas isso também é legal. Sua propriedade também seria parametrizada ou modelada.Não é de admirar que as estatísticas de VB sejam chamadas
shared
. Nesse caso, no entanto, você deve estar ciente de que esses membros "compartilhados" são compartilhados apenas entre instâncias da mesma classe exata e não entre as classes distintas produzidas pela substituição<T>
por outra coisa.fonte
Já existem várias boas respostas que explicam o aviso e o motivo. Vários desses afirmam algo como ter um campo estático em um tipo genérico geralmente um erro .
Pensei em acrescentar um exemplo de como esse recurso pode ser útil, ou seja, um caso em que suprimir o aviso R # faz sentido.
Imagine que você tem um conjunto de classes de entidade que deseja serializar, digamos em Xml. Você pode criar um serializador para isso usando
new XmlSerializerFactory().CreateSerializer(typeof(SomeClass))
, mas precisará criar um serializador separado para cada tipo. Usando genéricos, você pode substituir isso pelo seguinte, que pode ser inserido em uma classe genérica da qual as entidades podem derivar:Como você provavelmente não deseja gerar um novo serializador sempre que precisar serializar uma instância de um tipo específico, você pode adicionar o seguinte:
Se essa classe NÃO fosse genérica, cada instância da classe usaria a mesma
_typeSpecificSerializer
.Porém, como é genérico, um conjunto de instâncias do mesmo tipo
T
compartilhará uma única instância de_typeSpecificSerializer
(que será criada para esse tipo específico), enquanto instâncias com um tipo diferente deT
usarão instâncias diferentes de_typeSpecificSerializer
.Um exemplo
Forneceu as duas classes que se estendem
SerializableEntity<T>
:... vamos usá-los:
Nesse caso, sob o capô,
firstInst
esecondInst
haverá instâncias da mesma classe (a saberSerializableEntity<MyFirstEntity>
) e, como tal, eles compartilharão uma instância de_typeSpecificSerializer
.thirdInst
efourthInst
são exemplos de uma classe diferente (SerializableEntity<OtherEntity>
), e por isso irão partilhar um exemplo de_typeSpecificSerializer
que é diferente das outras duas.Isso significa que você obter diferentes serializador-instances para cada um de seus entidade tipos , mantendo-los estáticos dentro do contexto de cada tipo real (ou seja, compartilhada entre os casos que são de um tipo específico).
fonte