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;
}
}
Czy to źle? Zakładałbym, że to rzeczywiście ma static readonly
pole dla każdej możliwej EnumRouteConstraint<T>
instancji.
Odpowiedzi:
Dobrze jest mieć pole statyczne w typie ogólnym, o ile wiesz, że naprawdę otrzymasz jedno pole na kombinację argumentów typu. Domyślam się, że R # ostrzega cię na wypadek, gdybyś nie był tego świadomy.
Oto przykład:
Jak widać, pole
Generic<string>.Foo
jest inne niżGeneric<object>.Foo
- posiadają osobne wartości.źródło
class BaseFoo
zawierający element statyczny, to czy z niego wywnioskujesz,class Foo<T>: BaseFoo
czy wszystkieFoo<T>
klasy mają tę samą wartość elementu statycznego?Z wiki JetBrains :
źródło
To niekoniecznie jest błąd - ostrzega przed potencjalnym nieporozumieniem z generycznymi C #.
Najprostszym sposobem na zapamiętanie, co robią ogólne, jest: Ogólne to „plany” do tworzenia klas, podobnie jak klasy to „plany” do tworzenia obiektów. (Cóż, jest to jednak uproszczenie. Możesz także użyć ogólnych metod.)
Z tego punktu widzenia
MyClassRecipe<T>
nie jest to klasa - to przepis, schemat tego, jak wyglądałaby twoja klasa. Gdy podstawisz T czymś konkretnym, powiedzmy int, string itp., Otrzymasz klasę. Zgodne z prawem jest zadeklarowanie elementu statycznego (pole, właściwość, metoda) w nowo utworzonej klasie (jak w każdej innej klasie) i brak oznak błędu. Na pierwszy rzut oka byłoby to trochę podejrzane, jeśli zadeklarujeszstatic MyStaticProperty<T> Property { get; set; }
w planie klasowym, ale jest to również legalne. Twoja właściwość również zostałaby sparametryzowana lub szablonowana.Nic dziwnego, że w VB są nazywane
shared
. W takim przypadku należy jednak pamiętać, że tacy „współdzieleni” członkowie są dzieleni tylko między instancjami tej samej dokładnej klasy, a nie między odrębnymi klasami wytworzonymi przez zastąpienie<T>
czymś innym.źródło
Jest już kilka dobrych odpowiedzi, które wyjaśniają ostrzeżenie i jego przyczynę. Kilka z nich stwierdza coś w rodzaju błędu statycznego w typie ogólnym, generalnie błąd .
Pomyślałem, że dodam przykład, w jaki sposób ta funkcja może być użyteczna, tj. Przypadek, w którym tłumienie ostrzeżenia R # ma sens.
Wyobraź sobie, że masz zestaw klas jednostek, które chcesz serializować, powiedz Xml. Możesz utworzyć do tego celu serializator
new XmlSerializerFactory().CreateSerializer(typeof(SomeClass))
, ale wtedy będziesz musiał utworzyć osobny serializator dla każdego typu. Używając ogólnych, możesz zamienić to na następujące, które możesz umieścić w ogólnej klasie, z której jednostki mogą pochodzić:Ponieważ prawdopodobnie nie chcesz generować nowego serializatora za każdym razem, gdy musisz serializować wystąpienie określonego typu, możesz dodać to:
Jeśli ta klasa NIE byłaby ogólna, wówczas każda instancja klasy użyłaby tego samego
_typeSpecificSerializer
.Ponieważ jest to jednak rodzaj ogólny, zestaw instancji tego samego typu dla
T
będzie współużytkować pojedyncze wystąpienie_typeSpecificSerializer
(które zostanie utworzone dla tego określonego typu), podczas gdy wystąpienia o innym typie dlaT
będą używać różnych wystąpień_typeSpecificSerializer
.Przykład
Pod warunkiem, że dwie klasy, które rozszerzają
SerializableEntity<T>
:... użyjmy ich:
W tym przypadku, w obszarze okapu,
firstInst
isecondInst
będzie instancje tej samej klasy (tjSerializableEntity<MyFirstEntity>
), i jako takie, będą dzielić wystąpienie_typeSpecificSerializer
.thirdInst
ifourthInst
przypadki, w innej klasy (SerializableEntity<OtherEntity>
), a więc będą dzielić instancję_typeSpecificSerializer
to różni się od pozostałych dwóch.Oznacza to, że otrzymujesz różne instancje serializatora dla każdego z typów jednostek , jednocześnie zachowując je statycznie w kontekście każdego rzeczywistego typu (tj. Współużytkowane przez instancje określonego typu).
źródło