¿Indexadores estáticos?

119

¿Por qué no se permiten los indexadores estáticos en C #? No veo ninguna razón por la que no deban 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 no se permiten indexadores estáticos. ¿Por qué esto es tan?

Malfist
fuente
A continuación, quiero una implementación directa de IEnumerable en una clase estática, así que puedo hacerlo foreach (var enum in Enum):)
nawfal

Respuestas:

72

La notación del indexador requiere una referencia a this. Dado que los métodos estáticos no tienen una referencia a ninguna instancia particular de la clase, no puede usarlos thisy, en consecuencia, no puede usar la notación indexadora en métodos estáticos.

La solución a su problema es usar un patrón 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 puedes llamar Utilities.ConfigurationManager["someKey"]usando la notación indexadora.

Julieta
fuente
110
Pero, ¿por qué el indexador tiene que usar "esto"? No tiene que acceder a los datos de la instancia
Malfist
80
+1 por el comentario de Malfist. El hecho de que use "this" para un indexador de instancias no significa que no puedan encontrar otra sintaxis.
Jon Skeet
40
Convenido. Estás rogando la pregunta. Básicamente, ha dicho que la razón por la que no está permitido es porque no está permitido. -1 porque la pregunta era "¿por qué no está permitido?"
xr280xr
15
@ xr280xr +1 Para el uso correcto de "mendigar la pregunta" :) Además, tengo la misma queja.
RedFilter
14
-1 porque esta respuesta asume que la notación actual es la única notación posible si se implementara un indexador estático. El uso de thisen un indexador no es necesariamente obligatorio, probablemente se eligió por encima de otras palabras clave porque tenía más sentido. Para una implementación estática, la sintaxis siguiente puede ser muy viable: public object static[string value]. No es necesario utilizar la palabra clave thisen un contexto estático.
einsteinsci
91

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

Tendría que comprobarlo, pero sospecho que ya está disponible en IL (idioma intermedio).

Jon Skeet
fuente
6
Lenguaje intermedio: una especie de lenguaje ensamblador para .NET.
Jon Skeet
15
Lo que me trajo aquí es que tengo una clase personalizada que expone un diccionario de valores comunes utilizados en toda mi aplicación a través de una propiedad estática. Esperaba usar un indexador estático para acortar el acceso desde GlobalState.State [KeyName] a solo GlobalState [KeyName]. Hubiera sido bueno.
xr280xr
1
FWIW, cambiar instancea staticen IL para una propiedad y un método de obtención en una propiedad predeterminada da como resultado que ilasm se queje syntax error at token 'static'; No soy muy bueno para entrometerme en los asuntos de IL, pero eso suena al menos como un no inicial.
Amazingant
8

Como solución alternativa, puede definir un indexador de instancias en un objeto singleton / estático (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 ... }
}
ChrisW
fuente
1

También necesitaba (bueno, más bien tenerlo) de un indexador estático para almacenar atributos, así que descubrí una solución un tanto incómoda:

Dentro de la clase que desea tener un indexador estático (aquí: Elemento), cree una subclase del mismo nombre + "Dict". Déle una instancia estática de solo lectura como instancia de dicha subclase, y luego agregue su indexador deseado.

Por último, agregue la clase como importación estática (de ahí la subclase para exponer solo 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 como diccionario:

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

Pero, por desgracia, si uno realmente usara el objeto como "valor" -Tipo, entonces lo siguiente sería aún más corto (al menos como declaración), y también proporcionaría encasillado inmediato:

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);
DW.com
fuente
0

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

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

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

vGHazard
fuente
-2

La palabra clave this se refiere a la instancia actual de la clase. Las funciones miembro estáticas no tienen un puntero this. La palabra clave this se puede usar para acceder a miembros desde dentro de constructores, métodos de instancia y accesores de instancia (recuperado de msdn ). Dado que esto hace referencia a una instancia de la clase, entra en conflicto con la naturaleza de static, ya que static no está asociado con una instancia de la clase.

Una solución alternativa sería la siguiente, que le permite usar el indexador contra un diccionario privado, 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 le permite omitir todo el acceso a un miembro de la clase y simplemente crear una instancia e indexarlo.

    new ConfigurationManager()["ItemName"]
lamorach
fuente
4
Es una solución interesante, pero 1) introduce efectos secundarios (creación de un objeto de instancia vacío) que podría provocar presión de memoria y fragmentación en algunos entornos, 2) los caracteres adicionales desperdiciados por new ()podrían haberse utilizado para el nombre de calificador de un singleton en cambio, como.Current
Lawrence Ward
1
Al igual que la respuesta de Juliet , esto no responde a la pregunta de por qué no se admiten los indexadores estáticos. Primero, la pregunta no restringe el término "indexador estático" a "algo que use la thispalabra clave", y segundo, thisen la sintaxis, public string this[int index]estrictamente hablando, ni siquiera es un uso de un thispuntero (como puede ocurrir en el cuerpo de los métodos de instancia) , pero solo otro uso del token this . La sintaxis public static string this[int index]puede parecer un poco contradictoria, pero aún así sería inequívoca.
OR Mapper
2
@ORMapper También podría ser public static string class[int index].
Jim Balter
Supongo que estoy confundido, pensé 'La palabra clave this se refiere a la instancia actual de la clase. Las funciones miembro estáticas no tienen un puntero this. ' estaba explicando que dado que no hay ningún objeto para que este puntero haga referencia, no se puede usar la referencia a él. Y también dije que msdn era quien usa esa definición. La cadena estática pública frente a la cadena pública nunca se superpondrá en cuanto a mi conocimiento debido al hecho de que uno accede al objeto de tipo genérico, mientras que el otro accede a un objeto de instancia.
lamorach
-2

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

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

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

Dentro de esto:

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

que no mejora el código de ninguna manera; no es más pequeño en muchas líneas de código, no es más fácil de 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 de ninguna manera es obvio que es una propiedad por la que está indexando, mientras que con ambos:

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

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

Recuerde que queremos escribir código que sea fácil (= rápido) de entender, no 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.

tarta
fuente
8
Discrepar. Eso es simplemente un punto conceptual. Y el código se ve mejor en mi opinión, aunque esa es simplemente mi opinión.
ouflak