Indexadores estáticos?

119

Por que os indexadores estáticos não são permitidos em C #? Não vejo razão para que eles não sejam permitidos e, além disso, podem ser muito úteis.

Por exemplo:

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

O código acima se beneficiaria muito com um indexador estático. No entanto, ele não será compilado porque indexadores estáticos não são permitidos. Porque isto é assim?

Malfist
fonte
Em seguida eu quero implementação IEnumerable direto na classe estática, para que eu possa fazer foreach (var enum in Enum):)
Nawfal

Respostas:

72

A notação do indexador requer uma referência a this. Como os métodos estáticos não têm uma referência a nenhuma instância específica da classe, você não pode usá this-los e, consequentemente, não pode usar a notação do indexador em métodos estáticos.

A solução para o seu problema está usando um padrão singleton da seguinte maneira:

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

Agora você pode ligar Utilities.ConfigurationManager["someKey"]usando a notação do indexador.

Julieta
fonte
110
Mas por que o indexador precisa usar 'this'? Ele não tem que dados de instância de acesso
Malfist
80
+1 no comentário de Malfist. Só porque ele usa "this" para um indexador de instância não significa que eles não poderiam criar outra sintaxe.
Jon Skeet
40
Acordado. Você está implorando a pergunta. Você basicamente disse que o motivo pelo qual não é permitido é porque não é permitido. -1 porque a pergunta era "por que não é permitido?"
Xr280xr
15
@ xr280xr +1 Para o uso correto de "implorando a pergunta" :) Além disso, tenho a mesma reclamação.
RedFilter
14
-1 porque esta resposta assume que a notação atual é a única notação possível se um indexador estático foi implementado. O uso de thisem um indexador não é necessariamente necessário, provavelmente foi escolhido acima de outras palavras-chave porque fazia mais sentido. Para uma implementação estática, a seguinte sintaxe pode ser bastante viável: public object static[string value]. Não é necessário usar a palavra-chave thisem um contexto estático.
Einsteinsci #
91

Eu acredito que foi considerado não muito útil. Eu acho que é uma pena também - um exemplo que costumo usar é a codificação, onde Encoding.GetEncoding("foo")poderia estar Encoding["Foo"]. Eu não acho que surgiria com muita frequência, mas, além de qualquer outra coisa, parece um pouco inconsistente não estar disponível.

Eu precisaria verificar, mas suspeito que já esteja disponível no IL (idioma intermediário).

Jon Skeet
fonte
6
Linguagem intermediária - tipo de linguagem assembly para .NET.
Jon Skeet
14
O que me trouxe aqui é que tenho uma classe personalizada que expõe um dicionário de valores comuns usados ​​em todo o meu aplicativo por meio de uma propriedade estática. Eu esperava usar um indexador estático para reduzir o acesso do GlobalState.State [KeyName] a apenas GlobalState [KeyName]. Teria sido legal.
Xr280xr
1
FWIW, mudar instancepara staticIL para uma propriedade e método getter em uma propriedade padrão resulta em reclamação de ilasmo syntax error at token 'static'; Eu não sou bom em me intrometer nos assuntos de IL, mas isso parece pelo menos um não inicial.
Amazingant 24/03/2015
8

Como solução alternativa, você pode definir um indexador de instância em um objeto singleton / estático (diga que o ConfigurationManager é um singleton, em vez de ser uma classe estática):

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

Eu também estava precisando (bem, mais parecido com bom) de um indexador estático para armazenar atributos, então descobri uma solução alternativa um tanto estranha:

Dentro da classe que você deseja ter um indexador estático (aqui: Element), crie uma subclasse com o mesmo nome + "Dict". Dê uma estática somente leitura como instância da referida subclasse e adicione o indexador desejado.

Por fim, adicione a classe como importação estática (daí a subclasse para expor apenas o 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;
        }
    }
}

e, em seguida, você pode usá-lo em maiúscula como Tipo ou sem como dicionário:

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

Mas, infelizmente, se alguém realmente usar o objeto como "value" -Type, o abaixo será ainda mais curto (pelo menos como declaração) e também fornecerá o Typecasting imediato:

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
fonte
0

Com as construções mais recentes em C # 6, você pode simplificar o padrão singleton com um corpo de expressão de propriedade. Por exemplo, usei o seguinte atalho que funciona muito bem com a lente de código:

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

Ele tem o benefício adicional de poder ser encontrado e substituível para atualizar códigos mais antigos e unificar o acesso às configurações do aplicativo.

vGHazard
fonte
-2

A palavra-chave this refere-se à instância atual da classe. As funções de membro estático não possuem um ponteiro this. A palavra-chave this pode ser usada para acessar membros de construtores, métodos de instância e acessadores de instância. (Recuperados de msdn ). Como isso faz referência a uma instância da classe, ela entra em conflito com a natureza da estática, pois estática não está associada a uma instância da classe.

Uma solução alternativa seria a seguinte, que permite que você use o indexador em um Dicionário particular, portanto, você só precisa criar uma nova instância e acessar a 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>();        
}

Isso permite que você pule todo o acesso a um membro da classe e apenas crie uma instância e o indexe.

    new ConfigurationManager()["ItemName"]
Lamorach
fonte
4
é uma solução interessante, mas 1) apresenta efeitos colaterais (criação de um objeto de instância vazio) que podem levar à pressão e fragmentação da memória em alguns ambientes; 2) os caracteres extras desperdiçados por new ()poderiam ter sido usados ​​para o nome qualificador de um singleton em vez disso, como.Current
Lawrence Ward
1
Assim como a resposta de Julieta , isso não responde à pergunta por que os indexadores estáticos não são suportados. Primeiro, a pergunta não restringe o termo "indexador estático" a "algo que usa a thispalavra - chave" e, segundo, thisna sintaxe, public string this[int index]estritamente falando, nem mesmo o uso de um thisponteiro (como pode ocorrer no corpo dos métodos de instância) , mas apenas outro uso do token this . A sintaxe public static string this[int index]pode parecer um pouco contra-intuitiva, mas ainda assim seria inequívoca.
OR Mapper
2
@ORMapper Poderia muito bem ser public static string class[int index].
Jim Balter
Acho que estou confuso, pensei: 'A palavra-chave this se refere à instância atual da classe. As funções de membro estático não têm um ponteiro para esse '. estava explicando que, como não há objeto para o ponteiro fazer referência, você não pode usar a referência a ele. E também afirmei que foi o msdn quem usou essa definição. public static string vs. public string nunca se sobrepõe ao meu conhecimento, porque um acessa o objeto de tipo genérico, enquanto o outro acessa um objeto de instância.
lamorach
-2

O motivo é que é muito difícil entender exatamente o que você está indexando com um indexador estático.

Você diz que o código se beneficiaria de um indexador estático, mas seria mesmo? Tudo o que faria é mudar isso:

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

Nisso:

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

que não melhora o código de forma alguma; não é menor por muitas linhas de código, não é mais fácil escrever graças ao preenchimento automático e é menos claro, pois esconde o fato de que você está recebendo e definindo algo que chama de 'Propriedade' e na verdade força o leitor a vá ler a documentação sobre o que exatamente o indexador retorna ou define, porque não é de forma alguma óbvio que é uma propriedade para a qual você está indexando, enquanto com os dois:

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

Você pode ler em voz alta e entender imediatamente o que o código faz.

Lembre-se de que queremos escrever um código que seja fácil (= rápido) de entender, não um código que seja rápido de escrever. Não confunda a velocidade com a qual você pode especificar o código com a velocidade com a qual você conclui os projetos.

torta
fonte
8
Discordo. Isso é apenas um ponto conceitual. E o código parece melhor na minha opinião, embora seja apenas minha opinião.
ouflak