Statik İndeksleyiciler?


119

C # 'da statik indeksleyicilere neden izin verilmiyor? İzin verilmemesi için hiçbir neden göremiyorum ve dahası çok faydalı olabilirler.

Örneğin:

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

Yukarıdaki kod, statik bir indeksleyiciden büyük ölçüde faydalanacaktır. Ancak, statik dizinleyicilere izin verilmediğinden derlenmez. Bu neden böyle?


Sonra, statik sınıf üzerinde doğrudan IEnumerable uygulamasını istiyorum, böylece yapabilirim foreach (var enum in Enum):)
nawfal

Yanıtlar:


72

Dizin oluşturucu gösterimi için bir başvuru gerekir this. Statik yöntemlerin sınıfın herhangi bir örneğine başvurusu olmadığından this, onlarla birlikte kullanamazsınız ve sonuç olarak statik yöntemlerde dizinleyici gösterimini kullanamazsınız.

Sorununuzun çözümü aşağıdaki gibi bir tekil kalıp kullanmaktır:

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

Artık Utilities.ConfigurationManager["someKey"]indeksleyici gösterimini kullanarak çağrı yapabilirsiniz .


110
Ama indeksleyici neden 'bunu' kullanmak zorunda? Örnek verilerine erişmek zorunda değil
Malfist

80
Malfist'in yorumu için +1. Bir örnek dizinleyici için "bu" u kullanıyor olması, başka sözdizimi bulamayacakları anlamına gelmez.
Jon Skeet

40
Kabul. Soru için yalvarıyorsun. Temel olarak, buna izin verilmemesinin nedeninin izin verilmemesi olduğunu söylediniz. -1 çünkü soru "neden izin verilmiyor?"
xr280xr

15
@ xr280xr +1 "Soru sormak" ın doğru kullanımı için :) Ayrıca aynı şikayete sahibim.
RedFilter

14
-1 çünkü bu yanıt, statik bir indeksleyici uygulandığında geçerli gösterimin tek olası gösterim olduğunu varsayar. Kullanımı thisbir dizinleyicideki mutlaka en mantıklı çünkü büyük olasılıkla diğer anahtar kelimeler üstünde seçildi, gerekli değildir. Statik uygulanması için aşağıdaki sözdizimi oldukça canlı olabilir: public object static[string value]. Anahtar kelimeyi thisstatik bir bağlamda kullanmaya gerek yoktur .
einsteinsci

91

Çok yararlı olmadığına inanıyorum. Bence bu da utanç verici - kullanma eğiliminde olduğum bir örnek Kodlama, nerede Encoding.GetEncoding("foo")olabilir Encoding["Foo"]. Çok sık ortaya çıkacağını sanmıyorum , ancak her şeyden başka, mevcut olmamak biraz tutarsız geliyor.

Kontrol etmem gerekecek, ancak IL'de (Orta Dil) zaten mevcut olduğundan şüpheleniyorum .


6
Ara Dil - .NET için bir tür derleme dili.
Jon Skeet

15
Beni buraya getiren şey, uygulamam boyunca kullanılan ortak değerlerin bir sözlüğünü statik bir özellik aracılığıyla ortaya çıkaran özel bir sınıfım olması. GlobalState.State [KeyName] 'den sadece GlobalState [KeyName]' e erişimi kısaltmak için statik bir indeksleyici kullanmayı umuyordum. Güzel olurdu.
xr280xr

1
FWIW değişen instanceiçin staticILASM yakınan bir varsayılan özellik sonuçları üzerinde özelliği ve alıcı yöntemi için IL syntax error at token 'static'; IL işlerine karışmakta pek iyi değilim ama bu en azından bir ilk hayır gibi geliyor.
Amazingant

8

Bir çözüm olarak, bir tekli / statik nesnede bir örnek dizin oluşturucu tanımlayabilirsiniz (ConfigurationManager'ın statik bir sınıf olmak yerine bir tekil olduğunu söyleyin):

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

1

Ayrıca öznitelikleri depolamak için statik bir indeksleyiciye de ihtiyacım vardı (daha çok sahip olmak gibi), bu yüzden biraz garip bir çözüm buldum:

Statik bir indeksleyiciye sahip olmak istediğiniz sınıf içinde (burada: Element), aynı isimde + "Dict" bir alt sınıf oluşturun. Söz konusu alt sınıfın bir örneği olarak salt okunur bir statik verin ve ardından istediğiniz dizinleyiciyi ekleyin.

Son olarak, sınıfı statik içe aktarma olarak ekleyin (bu nedenle alt sınıf yalnızca statik alanı açığa çıkaracaktır).

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

ve sonra bunu Tür olarak büyük harfle veya sözlük olmadan kullanabilirsiniz:

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

Ancak ne yazık ki, eğer biri gerçekten "value" -Type olarak nesneyi kullanırsa, o zaman aşağıdakiler daha kısa olur (en azından bildirim olarak) ve ayrıca anında Typecasting sağlar:

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

C # 6'daki daha yeni yapılarla, tekli modeli bir özellik ifade gövdesi ile basitleştirebilirsiniz. Örneğin, kod merceğiyle iyi çalışan aşağıdaki kısayolu kullandım:

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

Eski kodu yükseltmek ve uygulama ayarları erişiminizi birleştirmek için bul-değiştirilebilme ek avantajına sahiptir.


-2

This anahtar sözcüğü sınıfın mevcut örneğini ifade eder. Statik üye işlevlerinde bu işaretçi yoktur. This anahtar sözcüğü yapıcılar, örnek yöntemleri ve örnek erişimciler içinden üyelere erişmek için kullanılabilir ( msdn'den alınır ). Bu, sınıfın bir örneğine başvurduğundan, statik, sınıfın bir örneğiyle ilişkilendirilmediğinden, static niteliğiyle çelişir.

Bir geçici çözüm, indeksleyiciyi özel bir Sözlüğe karşı kullanmanıza izin veren aşağıdaki olabilir, böylece yalnızca yeni bir örnek oluşturmanız ve statik parçaya erişmeniz gerekir.

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

Bu, sınıfın bir üyesine erişimin tamamını atlamanıza ve sadece bir örneğini oluşturmanıza ve dizine eklemenize olanak tanır.

    new ConfigurationManager()["ItemName"]

4
bu ilginç bir geçici çözüm, ancak 1) bazı ortamlarda bellek baskısına ve parçalanmaya yol açabilecek yan etkiler (boş bir örnek nesnenin oluşturulması) getiriyor, 2) harcanan ekstra karakterler new (), bir singleton niteleyici adı için kullanılmış olabilir bunun yerine.Current
Lawrence Ward

1
Tıpkı Juliet'in cevabı gibi , bu, statik indeksleyicilerin neden desteklenmediği sorusuna cevap vermiyor. Birincisi, soru "statik indeksleyici" terimini " thisanahtar kelimeyi kullanan bir şey" ile sınırlamaz ve ikincisi, thissözdiziminde public string this[int index]kesinlikle bir thisişaretçi kullanımı bile değildir (örnek yöntemlerinin gövdesinde meydana gelebileceği gibi) , ancak jetonun başka bir kullanımı this. Sözdizimi public static string this[int index]olabilir bakmak biraz mantığa aykırı, ama yine de kesin olurdu.
VEYA Mapper

2
@ORMapper Olabilir public static string class[int index].
Jim Balter

Sanırım kafam karıştı 'Bu anahtar kelime sınıfın mevcut örneğini ifade ediyor. Statik üye işlevlerinde bu işaretçi yoktur. ' bu işaretçinin referans vereceği nesne olmadığından, ona referansı kullanamayacağınızı açıklıyordu. Ayrıca msdn'nin bu tanımı kullanan kişi olduğunu belirttim. public static string vs public string, biri genel tip nesneye erişirken diğeri bir örnek nesnesine erişeceği gerçeğinden dolayı benim bilgimle asla örtüşmeyecek.
lamorach

-2

Nedeni, statik bir indeksleyici ile tam olarak neyi indekslediğinizi anlamanın oldukça zor olmasıdır.

Kodun statik bir indeksleyiciden yararlanacağını söylüyorsunuz, ama gerçekten olur mu? Tek yapacağı bunu değiştirmek:

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

Bunun içine:

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

bu, kodu hiçbir şekilde daha iyi yapmaz; birçok kod satırı tarafından küçültülmez, otomatik tamamlama sayesinde yazmak daha kolay değildir ve daha az nettir, çünkü 'Özellik' dediğiniz bir şeyi aldığınız ve ayarladığınız gerçeğini gizler ve aslında okuyucuyu gidin, indeksleyicinin tam olarak neyi döndürdüğüne veya ayarladığına dair dokümantasyonu okuyun, çünkü her ikisiyle de indekslediğiniz bir özellik olduğu hiçbir şekilde açık değildir:

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

Yüksek sesle okuyabilir ve kodun ne yaptığını hemen anlayabilirsiniz.

Yazması hızlı bir kod değil, anlaşılması kolay (= hızlı) kod yazmak istediğimizi unutmayın. Kodu yerleştirme hızınız ile projeleri tamamlama hızınızı karıştırmayın.


8
Katılmıyorum. Bu sadece kavramsal bir nokta. Ve bence kod daha iyi görünüyor, ancak bu sadece benim fikrim.
ouflak
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.