c# - ReSharper advierte: "Campo estático en tipo genérico"



2 Answers

Desde el wiki de JetBrains :

En la gran mayoría de los casos, tener un campo estático en un tipo genérico es un signo de error. La razón de esto es que un campo estático en un tipo genérico no se compartirá entre las instancias de diferentes tipos construidos cercanos. Esto significa que para una clase genérica C<T> que tiene un campo estático X , los valores de C<int>.X y C<string>.X tienen valores completamente diferentes e independientes.

En los casos excepcionales en los que necesite los campos estáticos 'especializados', no dude en suprimir la advertencia.

Si necesita tener un campo estático compartido entre instancias con diferentes argumentos genéricos, defina una clase base no genérica para almacenar sus miembros estáticos, luego configure su tipo genérico para heredar de este tipo.

c# asp.net-mvc-3 generics static resharper
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 en realidad tiene un campo de static readonly para cada una de las posibles EnumRouteConstraint<T> que suceda a la instancia.




Ya hay varias respuestas buenas aquí, que explican la advertencia y la razón de ello. Varios de estos estados dicen que tener un campo estático en un tipo genérico generalmente es 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 de R # tiene sentido.

Imagina que tienes un conjunto de clases de entidad que quieres serializar, por ejemplo a Xml. Puede crear un serializador para esto utilizando el 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 que las entidades pueden derivar de:

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

Como probablemente no desee generar un nuevo serializador cada vez que necesite serializar una instancia de un tipo en particular, puede agregar esto:

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

Si esta clase NO fuera genérica, entonces cada instancia de la clase usaría el mismo _typeSpecificSerializer .

Sin embargo, dado que es genérico, un conjunto de instancias con el mismo tipo para T compartirá una sola instancia de _typeSpecificSerializer (que se habrá creado para ese tipo específico), mientras que las instancias con un tipo diferente para T usarán diferentes instancias de _typeSpecificSerializer .

Un ejemplo

Proporcionó las dos clases que extienden 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; }
}

... vamos a usarlos:

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

En este caso, bajo el capó, firstInst y firstInst serán instancias de la misma clase (es decir, SerializableEntity<MyFirstEntity> ), y como tal, compartirán una instancia de _typeSpecificSerializer .

thirdInst y fourthInst son instancias de una clase diferente ( SerializableEntity<OtherEntity> ), por lo que compartirán una instancia de _typeSpecificSerializer que es diferente de las otras dos.

Esto significa que obtiene diferentes instancias de serializador para cada uno de sus tipos de entidades, mientras que las mantiene estáticas dentro del contexto de cada tipo real (es decir, compartido entre instancias que son de un tipo específico).






Related