web-dev-qa-db-ja.com

静的インデクサー?

静的インデクサーが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];
        }
    }

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

111
Malfist

インデクサー表記には、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"]インデクサー表記を使用します。

66
Juliet

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

確認する必要がありますが、私は疑わしい IL(Intermediate Language)で既に利用可能です。

87
Jon Skeet

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

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

C#6の新しいコンストラクトでは、プロパティ式の本文を使用してシングルトンパターンを単純化できます。たとえば、コードレンズでうまく機能する次のショートカットを使用しました。

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

古いコードをアップグレードし、アプリケーション設定へのアクセスを統一するために、find-replace-ableであるという追加の利点があります。

0
vGHazard

また、属性を保存するための静的インデクサーが必要でした(まあ、もっといい感じです)ので、やや厄介な回避策を見つけました:

静的インデクサー(ここでは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;
        }
    }
}

そして、Typeとして大文字で使用するか、辞書として使用しないで使用できます:

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
DW.com