ReSharper ostrzega: "pole statyczne w typie generycznym"

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ładam, że to rzeczywiście ma pole static readonly dla każdego z możliwych EnumRouteConstraint<T>, które zdarza mi się wystosować.
Author: Bo Persson, 2012-03-10

4 answers

Dobrze jest mieć statyczne pole w typie ogólnym, o ile wiesz, że naprawdę dostaniesz jedno pole na kombinację argumentów typu. Zgaduję, że R# ostrzega cię, gdybyś nie był tego świadomy.

Oto przykład:

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
    }
}

Jak widać, Generic<string>.Foo jest innym polem niż Generic<object>.Foo - posiadają osobne wartości.

 399
Author: Jon Skeet,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-10-02 10:51:40

Z JetBrains wiki :

W zdecydowanej większości przypadków, mając pole statyczne w typie ogólnym to oznaka błędu. Powodem tego jest to, że pole statyczne w Typ generyczny będzie , a nie współdzielony pomiędzy instancjami o różnych bliskich skonstruowane typy. Oznacza to, że dla klasy generycznej C<T>, która posiada pole Statyczne X, wartości C<int>.X i C<string>.X mają zupełnie inne, niezależne wartości.

W rzadkich przypadkach kiedy robisz potrzebujesz 'wyspecjalizowanych' pól statycznych, nie krępuj się tłumić Ostrzeżenia.

Jeśli chcesz mieć pole statyczne współdzielone między instancjami z różne argumenty generyczne, zdefiniuj nie-generyczną klasę bazową do przechowuj statyczne elementy, a następnie Ustaw typ generyczny na dziedziczenie od ten typ.

 135
Author: AakashM,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-03-12 10:13:01

To niekoniecznie błąd-ostrzega Cię o potencjale niezrozumienie C# generics.

Najprostszym sposobem na zapamiętanie tego, co robią leki generyczne, jest następujący sposób: Generyki są "schematami" do tworzenia klas, podobnie jak klasy są "schematami" do tworzenia obiektów. (Cóż, jest to jednak uproszczenie. Możesz również użyć metod generycznych.)

Z tego punktu widzenia {[0] } to nie jest klasa - to jest przepis, schemat, co twoja klasa na to wygląda. Po zastąpieniu T czymś konkretnym, powiedzmy int, string itp. masz zajęcia. Jest całkowicie legalne mieć statyczny element (pole, właściwość, metoda) zadeklarowany w nowo utworzonej klasie (jak w każdej innej klasie) i nie ma żadnych oznak błędu tutaj. Na pierwszy rzut oka byłoby to podejrzane, gdybyś zadeklarował static MyStaticProperty<T> Property { get; set; } w swoim schemacie klasowym, ale to też jest legalne. Twoja nieruchomość będzie również parametryzowana lub szablonowa.

Nic dziwnego, że w statyce VB nazywa się shared. W tym przypadku powinieneś jednak mieć świadomość, że takie" wspólne " elementy są dzielone tylko pomiędzy instancjami tej samej klasy, a nie pomiędzy odrębnymi klasami powstałymi przez zastąpienie <T> czymś innym.

 56
Author: Alexander Christov,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-06-07 18:37:45

Jest tu już kilka dobrych odpowiedzi, które wyjaśniają Ostrzeżenie i powód. Kilka z nich stwierdza coś w rodzaju posiadanie statycznego pola w typie ogólnym ogólnie jest błędem .

Pomyślałem, że dodam przykład, jak ta funkcja może być przydatna, np. przypadek, w którym tłumienie Ostrzeżenia R#ma sens.

Wyobraź sobie, że masz zestaw klas encji, które chcesz serializować, powiedzmy do Xml. Możesz utworzyć do tego serializer używając new XmlSerializerFactory().CreateSerializer(typeof(SomeClass)), ale następnie będziesz musiał utworzyć oddzielny serializer dla każdego typu. Używając generyków, możesz zastąpić je poniższą, którą możesz umieścić w klasie generycznej, z której mogą wywodzić się encje:

new XmlSerializerFactory().CreateSerializer(typeof(T))

Ponieważ prawdopodobnie nie chcesz generować nowego serializera za każdym razem, gdy musisz serializować wystąpienie określonego typu, możesz dodać to:

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);
     }
}

Gdyby ta klasa nie była generyczna, wtedy każda instancja klasy używałaby tego samego _typeSpecificSerializer.

Ponieważ jest generic jednak, zbiór wystąpień o tym samym typie dla T będzie współdzielić jedną instancję _typeSpecificSerializer (która zostanie utworzona dla tego konkretnego typu), podczas gdy wystąpienia o innym typie dla T będą używać różnych wystąpień _typeSpecificSerializer.

Przykład

Pod warunkiem, że dwie klasy rozszerzające 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; }
}

... użyjmy ich:

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();

W tym przypadku, pod maską, firstInst i secondInst będą instancjami tej samej klasy (mianowicie SerializableEntity<MyFirstEntity>), a jako takie, będą dzielić instancję _typeSpecificSerializer.

thirdInst i fourthInst są instancjami innej klasy (SerializableEntity<OtherEntity>), a więc będą współdzielić instancję _typeSpecificSerializer, która jest Inna od dwóch pozostałych.

Oznacza to, że otrzymujesz różne instancje serializera dla każdego z typów encji , zachowując je statyczne w kontekście każdego rzeczywistego typu (tj. współdzielone między instancjami danego typu).

 7
Author: Kjartan,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-07-22 06:50:11