Pregunta

¿Por qué no se permiten los indexadores estáticos en C #? No veo ninguna razón por la que no deberían permitirse y, además, podrían ser muy útiles.

Por ejemplo:

public static class ConfigurationManager 
{
        public object this[string name]
        {
            get => ConfigurationManager.getProperty(name);
            set => ConfigurationManager.editProperty(name, value);
        }

        /// <summary>
        /// This will write the value to the property. Will overwrite if the property is already there
        /// </summary>
        /// <param name="name">Name of the property</param>
        /// <param name="value">Value to be wrote (calls ToString)</param>
        public static void editProperty(string name, object value) 
        {
            var ds = new DataSet();
            var configFile = new FileStream("./config.xml", FileMode.OpenOrCreate);
            ds.ReadXml(configFile);

            if (ds.Tables["config"] == null)
                ds.Tables.Add("config");

            var config = ds.Tables["config"];

            if (config.Rows[0] == null) 
                config.Rows.Add(config.NewRow());

            if (config.Columns[name] == null) 
                config.Columns.Add(name);

            config.Rows[0][name] = value.ToString();

            ds.WriteXml(configFile);
            configFile.Close();
        }

        public static void addProperty(string name, object value) =>
            ConfigurationManager.editProperty(name, value);

        public static object getProperty(string name) 
        {
            var ds = new DataSet();
            var configFile = new FileStream("./config.xml", FileMode.OpenOrCreate);
            ds.ReadXml(configFile);
            configFile.Close();

            if (ds.Tables["config"] == null) return null;

            var config = ds.Tables["config"];

            if (config.Rows[0] == null) return null;
            if (config.Columns[name] == null) return null;

            return config.Rows[0][name];
        }
    }

El código anterior se beneficiaría enormemente de un indexador estático. Sin embargo, no se compilará porque los indexadores estáticos no están permitidos. ¿Por qué esto es así?

¿Fue útil?

Solución

La notación del indexador requiere una referencia a this . Como los métodos estáticos no tienen una referencia a una instancia particular de la clase, no puede usar esto con ellos, y por lo tanto no puede usar la notación de indexador en métodos estáticos.

La solución a su problema es usar un patrón de singleton de la siguiente manera:

public class Utilities
{
    private static ConfigurationManager _configurationManager = new ConfigurationManager();
    public static ConfigurationManager ConfigurationManager => _configurationManager;
}
public class ConfigurationManager
{
    public object this[string value]
    {
        get => new object();
        set => // set something
    }
}

Ahora puede llamar a Utilities.ConfigurationManager [" someKey "] usando la notación de indexador.

Otros consejos

Creo que se consideró que no era terriblemente útil. Creo que también es una pena, un ejemplo que tiendo a usar es Codificación, donde Encoding.GetEncoding (" foo " ) podría ser Codificación [" Foo "] . No creo que surja muy a menudo, pero aparte de cualquier otra cosa, parece un poco inconsistente no estar disponible.

Tendría que verificar, pero sospecho ya está disponible en IL (Idioma intermedio)

Como solución alternativa, puede definir un indexador de instancia en un objeto singleton / static (digamos que ConfigurationManager es un singleton, en lugar de ser una clase estática):

class ConfigurationManager
{
  //private constructor
  ConfigurationManager() {}
  //singleton instance
  public static ConfigurationManager singleton;
  //indexer
  object this[string name] { ... etc ... }
}

Con las construcciones más nuevas en C # 6, puede simplificar el patrón de singleton con un cuerpo de expresión de propiedad. Por ejemplo, utilicé el siguiente método abreviado que funciona muy bien con la lente de código:

public static class Config
{
   public static NameValueCollection Get => ConfigurationManager.AppSettings;
}

Tiene el beneficio adicional de poder buscar y reemplazar para actualizar el código anterior y unificar el acceso a la configuración de la aplicación.

También estaba necesitado (bueno, más bien como agradable tener) un indexador estático para almacenar atributos, por lo que descubrí una solución un tanto torpe:

Dentro de la clase en la que desea tener un indexador estático (aquí: Elemento), cree una subclase con el mismo nombre + " Dict " ;. Dale un carácter de solo lectura como instancia de dicha subclase y luego agrega el indexador que desees.

Por último, agregue la clase como importación estática (de ahí que la subclase solo exponga el campo estático).

import static Element.ElementDict;

public class Element {
    // .... 
    private static readonly Dictionary<string, object> elemDict = new Dictionary<string, object>();
    public class ElementDict {
        public readonly static ElementDict element = new ElementDict();
        public object this[string key] {
            get => elemDict.TryGetValue(key, out object o) ? o : null;
            set => elemDict[key] = value;
        }
    }
}

y luego puede usarlo en mayúsculas como Tipo o sin diccionario:

var cnt = element["counter"] as int;
element["counter"] = cnt;

Pero, por desgracia, si uno utilizara el objeto como valor " valor " -Tipo, entonces el siguiente sería aún más corto (al menos como declaración), y también proporcionaría la difusión de tipos inmediata:

public static T load<T>(string key) => elemDict.TryGetValue(key, out object o) ? (T) o : default(T);
public static void store<T>(string key, T value) => elemDict[key] = value;

var cnt = Element.load<int>("counter");
Element.store("counter", cnt);

La palabra clave this se refiere a la instancia actual de la clase. Las funciones miembro estáticas no tienen este puntero. La palabra clave this se puede usar para acceder a los miembros desde constructores, métodos de instancia y accesores de instancia. 71% 29.ASPX "rel =" nofollow "> msdn ). Como esto hace referencia a una instancia de la clase, está en conflicto con la naturaleza de la estática, ya que la estática no está asociada con una instancia de la clase.

Una solución alternativa sería la siguiente, que le permite utilizar el indexador en contra de un privado Diccionario, por lo que solo necesita crear una nueva instancia y acceder a la parte estática.

    public class ConfigurationManager 
{
    public ConfigurationManager()
    {
        // TODO: Complete member initialization
    }
    public object this[string keyName]
    {
        get
        {
                return ConfigurationManagerItems[keyName];
        }
        set
        {
                ConfigurationManagerItems[keyName] = value;
        }
    }
    private static Dictionary<string, object> ConfigurationManagerItems = new Dictionary<string, object>();        
}

Esto te permite omitir todo el acceso a un miembro de la clase y simplemente crear una instancia de él e indexarlo.

    new ConfigurationManager()["ItemName"]

La razón es porque es bastante difícil entender qué es exactamente lo que está indexando con un indexador estático.

Usted dice que el código se beneficiaría de un indexador estático, pero ¿lo haría realmente? Todo lo que haría es cambiar esto:

ConfigurationManager.editProperty(name, value);
...
value = ConfigurationManager.getProperty(name)

En esto:

ConfigurationManager[name] = value
...
value = ConfigurationManager[name]

que no mejora el código de ninguna manera; no es más pequeño por muchas líneas de código, no es más fácil escribir gracias al autocompletar y es menos claro, ya que oculta el hecho de que está obteniendo y configurando algo que llama "Propiedad" y en realidad obliga al lector a Vaya a leer la documentación sobre qué devuelve o establece exactamente el indexador, porque no es de ninguna manera obvio que sea una propiedad para la que está indexando, mientras que con ambos:

<*>

Puede leerlo en voz alta y entender de inmediato lo que hace el código.

Recuerde que queremos escribir un código que sea fácil de entender (= rápido), no un código que sea rápido de escribir. No confunda la velocidad a la que puede establecer el código con la velocidad a la que completa los proyectos.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top