Pengindeks Statis?


119

Mengapa pengindeks statis tidak diizinkan di C #? Saya tidak melihat alasan mengapa mereka tidak diizinkan dan lebih jauh lagi mereka bisa sangat berguna.

Sebagai contoh:

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

Kode di atas akan sangat diuntungkan dari pengindeks statis. Namun itu tidak dapat dikompilasi karena pengindeks statis tidak diperbolehkan. Mengapa demikian?


Selanjutnya saya ingin implementasi IEnumerable langsung pada kelas statis, jadi saya bisa melakukannya foreach (var enum in Enum):)
nawfal

Jawaban:


72

Notasi pengindeks membutuhkan referensi ke this. Karena metode statis tidak memiliki referensi ke instance kelas tertentu, Anda tidak dapat menggunakannya this, dan akibatnya Anda tidak dapat menggunakan notasi pengindeks pada metode statis.

Solusi untuk masalah Anda menggunakan pola tunggal sebagai berikut:

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

Sekarang Anda dapat memanggil Utilities.ConfigurationManager["someKey"]menggunakan notasi pengindeks.


110
Tetapi mengapa pengindeks harus menggunakan 'ini'? Itu tidak harus mengakses data contoh
Malfist

80
+1 untuk komentar Malfist. Hanya karena menggunakan "ini" untuk pengindeks instance, bukan berarti mereka tidak dapat menemukan sintaks lain.
Jon Skeet

40
Sepakat. Anda memohon pertanyaan. Anda pada dasarnya mengatakan alasan itu tidak diperbolehkan adalah karena itu tidak diperbolehkan. -1 karena pertanyaannya adalah "mengapa tidak diperbolehkan?"
xr280xr

15
@ xr280xr +1 Untuk penggunaan yang benar dari "mengemis pertanyaan" :) Ditambah saya memiliki keluhan yang sama.
RedFilter

14
-1 karena jawaban ini mengasumsikan bahwa notasi saat ini adalah satu-satunya notasi yang mungkin jika pengindeks statis diterapkan. Penggunaan thisdalam pengindeks tidak selalu diperlukan, kemungkinan besar dipilih di atas kata kunci lain karena paling masuk akal. Untuk implementasi statis, sintaks berikut mungkin cukup layak: public object static[string value]. Tidak perlu menggunakan kata kunci thisdalam konteks statis.
einsteinsci

91

Saya yakin itu dianggap tidak terlalu berguna. Saya pikir itu memalukan juga - contoh yang cenderung saya gunakan adalah Pengkodean, di mana Encoding.GetEncoding("foo")bisa Encoding["Foo"]. Saya tidak berpikir itu akan datang sangat sering, tapi selain dari apa pun hanya terasa sedikit tidak konsisten tidak akan tersedia.

Saya harus memeriksanya, tetapi saya curiga itu sudah tersedia dalam IL (Intermediate Language).


6
Intermediate Language - semacam bahasa assembly untuk .NET.
Jon Skeet

15
Apa yang membawa saya ke sini adalah saya memiliki kelas khusus yang mengekspos kamus nilai-nilai umum yang digunakan di seluruh aplikasi saya melalui properti statis. Saya berharap untuk menggunakan pengindeks statis untuk mempersingkat akses dari GlobalState.State [KeyName] menjadi hanya GlobalState [KeyName]. Akan menyenangkan.
xr280xr

1
FWIW, mengubah instanceke staticdalam IL untuk properti dan metode pengambil pada properti default menghasilkan keluhan ilasm syntax error at token 'static'; Saya tidak pandai ikut campur dalam urusan IL tapi itu terdengar seperti setidaknya sebuah inisial tidak.
Amazingant

8

Sebagai solusinya, Anda dapat menentukan pengindeks instance pada objek tunggal / statis (katakanlah bahwa ConfigurationManager adalah tunggal, bukan kelas statis):

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

1

Saya juga membutuhkan (yah, lebih seperti bagus untuk dimiliki) pengindeks statis untuk menyimpan atribut, jadi saya menemukan solusi yang agak canggung:

Di dalam kelas Anda ingin memiliki pengindeks statis (di sini: Elemen), buat subclass dengan nama yang sama + "Dict". Berikan statis hanya baca sebagai instance dari subkelas tersebut, lalu tambahkan pengindeks yang Anda inginkan.

Terakhir, tambahkan kelas sebagai impor statis (oleh karena itu subkelas hanya mengekspos bidang statis).

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

lalu Anda dapat menggunakannya dengan huruf besar sebagai Jenis, atau tanpa sebagai kamus:

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

Namun sayangnya, jika seseorang benar-benar menggunakan objek sebagai "value" -Type, maka di bawah ini akan tetap lebih pendek (setidaknya sebagai deklarasi), dan juga menyediakan Typecasting langsung:

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

0

Dengan konstruksi yang lebih baru di C # 6, Anda mungkin menyederhanakan pola singleton dengan badan ekspresi properti. Misalnya, saya menggunakan pintasan berikut yang berfungsi baik dengan code-lense:

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

Ini memiliki manfaat tambahan karena dapat menemukan-ganti untuk meningkatkan kode lama dan menyatukan akses pengaturan aplikasi Anda.


-2

Kata kunci ini mengacu pada instance kelas saat ini. Fungsi anggota statis tidak memiliki penunjuk ini. Kata kunci ini dapat digunakan untuk mengakses anggota dari dalam konstruktor, metode instance, dan pengakses instance. (Diambil dari msdn ). Karena ini mereferensikan instance kelas, itu bertentangan dengan sifat statis, karena statis tidak terkait dengan instance kelas.

Salah satu solusinya adalah berikut ini yang memungkinkan Anda menggunakan pengindeks terhadap kamus pribadi sehingga Anda hanya perlu membuat instance baru dan mengakses bagian statis.

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

Ini memungkinkan Anda untuk melewati seluruh akses ke anggota kelas dan hanya membuat instance dari itu dan mengindeksnya.

    new ConfigurationManager()["ItemName"]

4
ini adalah solusi yang menarik, tetapi 1) ini memperkenalkan efek samping (pembuatan objek instance kosong) yang dapat menyebabkan tekanan memori dan fragmentasi di beberapa lingkungan, 2) karakter tambahan yang terbuang new ()dapat digunakan untuk nama kualifikasi dari sebuah singleton sebaliknya, seperti.Current
Lawrence Ward

1
Sama seperti jawaban Juliet , ini tidak menjawab pertanyaan mengapa pengindeks statis tidak didukung. Pertama, pertanyaannya tidak membatasi istilah "pengindeks statis" menjadi "sesuatu yang menggunakan thiskata kunci", dan kedua, thisdalam sintaks public string this[int index]secara tegas bahkan tidak menggunakan thispenunjuk (seperti yang mungkin terjadi dalam tubuh metode contoh) , tetapi hanya penggunaan token lainnya this . Sintaksnya public static string this[int index]mungkin terlihat agak berlawanan dengan intuisi, tetapi tetap tidak ambigu.
ATAU Mapper

2
@ORMap Bisa juga public static string class[int index].
Jim Balter

Saya kira saya bingung saya pikir 'Kata kunci ini mengacu pada contoh kelas saat ini. Fungsi anggota statis tidak memiliki penunjuk ini. ' menjelaskan bahwa karena tidak ada objek untuk referensi penunjuk ini, Anda tidak dapat menggunakan referensi ke sana. Dan saya juga menyatakan msdn-lah yang menggunakan definisi itu. string publik statis vs. string publik tidak akan pernah tumpang tindih dengan pengetahuan saya karena fakta yang satu mengakses objek tipe generik, sedangkan yang lain akan mengakses objek contoh.
lamorach

-2

Alasannya karena cukup sulit untuk memahami apa yang sebenarnya Anda indeks dengan pengindeks statis.

Anda mengatakan bahwa kode akan mendapatkan keuntungan dari pengindeks statis, tetapi apakah itu benar-benar? Yang akan dilakukannya hanyalah mengubah ini:

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

Menjadi ini:

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

yang tidak membuat kode menjadi lebih baik dengan cara apa pun; tidak lebih kecil dengan banyak baris kode, tidak lebih mudah untuk menulis berkat pelengkapan otomatis dan kurang jelas, karena menyembunyikan fakta bahwa Anda mendapatkan dan menyetel sesuatu yang Anda sebut 'Properti' dan itu benar-benar memaksa pembaca untuk baca dokumentasi tentang apa sebenarnya yang dikembalikan atau diset oleh pengindeks, karena sama sekali tidak jelas bahwa ini adalah properti yang Anda indeks, sementara dengan keduanya:

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

Anda dapat membacanya dengan lantang dan segera memahami fungsi kode tersebut.

Ingatlah bahwa kita ingin menulis kode yang mudah (= cepat) dipahami, bukan kode yang cepat ditulis. Jangan salahkan kecepatan Anda meletakkan kode dengan kecepatan Anda menyelesaikan proyek.


8
Tidak setuju. Itu hanyalah poin konseptual. Dan kodenya memang terlihat lebih baik menurut saya, meskipun itu hanya pendapat saya.
ouflak
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.