静的インデクサー?


119

C#で静的インデクサーが許可されないのはなぜですか?それらが許可されるべきではなく、さらにそれらが非常に有用である理由は私にはわかりません。

例えば:

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

上記のコードは、静的インデクサーから大きなメリットを得ます。ただし、静的インデクサーは許可されていないため、コンパイルされません。これはなぜですか?


次に、静的クラスに直接IEnumerableを実装したいので、次のようにしますforeach (var enum in Enum):)
nawfal

回答:


72

インデクサー表記には、への参照が必要thisです。静的メソッドはクラスの特定のインスタンスへの参照を持たないためthis、それらを使用することはできません。その結果、静的メソッドでインデクサー表記を使用することはできません。

問題の解決策は、次のようにシングルトンパターンを使用することです。

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

これでUtilities.ConfigurationManager["someKey"]、インデクサー表記を使用して呼び出すことができます。


110
しかし、なぜインデクサーは 'this'を使用する必要があるのでしょうか。インスタンスデータにアクセスする必要がない
Malfist 2008

80
Malfistのコメントに+1。インスタンスのインデクサーに「this」を使用しているからといって、他の構文を考え出せなかったという意味ではありません。
Jon Skeet

40
同意した。あなたは質問を懇願しています。基本的に、許可されていない理由は許可されていないからだとおっしゃっていました。質問は「なぜ許可されないのですか?」
xr280xr

15
@ xr280xr +1「物乞い」を正しく使用するため:)加えて、私は同じ不満を持っています。
RedFilter 2014

14
-1。この回答は、静的インデクサーが実装されている場合、現在の表記が唯一の可能な表記であると想定しているためです。thisインデクサーでのの使用は必ずしも必要ではありません。最も理にかなっているため、他のキーワードよりも上位に選択された可能性があります。静的な実装の場合、次の構文が非常に有効ですpublic object static[string value]this静的コンテキストでキーワードを使用する必要はありません。
einsteinsci 2015年

91

あまり役に立たないと考えられていたと思います。私もそれは残念だと思います-私が使用する傾向がある例は、どこにあるEncoding.GetEncoding("foo")かもしれないエンコーディングEncoding["Foo"]です。あまり頻繁に出てくるとは思いませんが、それ以外は、利用できないというのが少し矛盾しているように感じます。

私がチェックする必要がありますが、私は疑うことはすでにIL(中間言語)で利用可能です。


6
中間言語-.NETの一種のアセンブリ言語。
Jon Skeet

14
ここに来たのは、静的プロパティを介してアプリケーション全体で使用される一般的な値の辞書を公開するカスタムクラスがあることです。静的インデクサーを使用して、GlobalState.State [KeyName]からGlobalState [KeyName]へのアクセスを短縮することを望んでいました。よかったでしょう。
xr280xr

1
FWIW、デフォルトプロパティのプロパティおよびゲッターメソッドのIL でに変更instanceするstaticと、ilasmが不平を言いsyntax error at token 'static'ます。私はILの問題をいじるのが苦手ですが、それは少なくとも最初のノーのように思えます。
Amazingant 2015年

8

回避策として、シングルトン/静的オブジェクトにインスタンスインデクサーを定義できます(ConfigurationManagerが静的クラスではなくシングルトンであるとします)。

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

1

また、属性を格納するための静的インデクサーが必要だった(まあ、もっといいのではないか)ので、やや厄介な回避策を見つけました。

静的インデクサー(ここではElement)を使用するクラス内で、同じ名前のサブクラスと「Dict」を作成します。上記のサブクラスのインスタンスとして読み取り専用の静的を指定し、目的のインデクサーを追加します。

最後に、クラスを静的インポートとして追加します(したがって、サブクラスは静的フィールドのみを公開します)。

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

そして、あなたはそれをタイプとして大文字で使うか、辞書なしで使うことができます:

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

しかし、悲しいかな、実際にオブジェクトを「値」タイプとして使用する場合、以下は(少なくとも宣言として)さらに短くなり、即時型キャストも提供されます。

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の新しい構成では、プロパティ式の本文を使用してシングルトンパターンを簡略化できます。たとえば、コードレンズでうまく機能する次のショートカットを使用しました。

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

古いコードをアップグレードし、アプリケーション設定へのアクセスを統合するための検索置換可能であるという追加の利点があります。


-2

thisキーワードは、クラスの現在のインスタンスを参照します。静的メンバー関数にはthisポインターがありません。thisキーワードを使用して、コンストラクター、インスタンスメソッド、およびインスタンスアクセサー内からメンバーにアクセスできます(msdnから取得)。これはクラスのインスタンスを参照するため、staticはクラスのインスタンスに関連付けられていないため、staticの性質と競合します。

回避策の1つは、プライベートディクショナリに対してインデクサーを使用できるようにする次のようになるため、新しいインスタンスを作成して静的部分にアクセスするだけで済みます。

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

これにより、クラスのメンバーへのアクセス全体をスキップして、そのインスタンスを作成し、それにインデックスを付けることができます。

    new ConfigurationManager()["ItemName"]

4
これは興味深い回避策ですが、1)一部の環境でメモリの圧迫と断片化につながる可能性がある副作用(空のインスタンスオブジェクトの作成)が導入されます。2)無駄な余分な文字がnew ()シングルトンの修飾子名に使用された可能性があります。代わりに、.Current
ローレンスワード

1
Julietの回答と同様に、これは静的インデクサーがサポートされない理由の質問には答えません。最初に、質問は「静的インデクサー」という用語を「thisキーワードを使用するもの」に制限していません。this次に、構文でpublic string this[int index]は、厳密に言えばthisポインターの使用すらしていません(インスタンスメソッドの本体で発生する可能性があるため)。しかし、トークンの もう1つの使用法ですthis。構文public static string this[int index]は少し直観に反するように見えるかもしれません、それでもあいまいさはありません。
またはMapper、

2
@ORMapperそれもそうかもしれませんpublic static string class[int index]
ジムバルター、2015

「このキーワードはクラスの現在のインスタンスを参照しています。静的メンバー関数にはthisポインターがありません。thisへの参照へのポインタのオブジェクトがないため、それへの参照を使用できないことを説明していました。また、その定義を使用したのはmsdnであるとも述べました。パブリック静的文字列とパブリック文字列は、一方がジェネリック型オブジェクトにアクセスし、もう一方がインスタンスオブジェクトにアクセスするという事実により、私の知る限り重複することはありません。
lamorach

-2

その理由は、静的インデクサーを使用してインデックスを作成していることを正確に理解することが非常に難しいためです。

あなたはコードが静的インデクサーの恩恵を受けると言いますが、本当にそうでしょうか?それを行うすべてはこれを変更することです:

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

これに:

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

これにより、コードが改善されることはありません。それは多くのコード行で小さくはありません、オートコンプリートのおかげで書くのは簡単ではありません、そしてそれはあなたがあなたが「プロパティ」と呼ぶものを取得して設定しているという事実を隠し、実際に読者にインデクサーが正確に何を返すか、または設定するかについてのドキュメントを読んでください。これは、両方を使用しているときに、インデックスを作成しているプロパティであることは明らかではないためです。

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

あなたはそれを大声で読み、コードが何をするかをすぐに理解することができます。

書くのが速いコードではなく、理解しやすい(=速い)コードを書きたいということを忘れないでください。コードを配置できる速度とプロジェクトを完了する速度を混同しないでください。


8
同意しない。それは単なる概念的なポイントです。そして、私の意見ではコードはよりよく見えますが、それは単に私の意見です。
ouflak 2018
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.