【问题标题】:Static Indexers?静态索引器?
【发布时间】:2010-09-28 21:51:31
【问题描述】:

为什么在 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];
        }
    }

上述代码将从静态索引器中受益匪浅。但是它不会编译,因为不允许使用静态索引器。为什么会这样?

【问题讨论】:

    标签: c# .net static indexer static-indexers


    【解决方案1】:

    作为一种解决方法,您可以在单例/静态对象上定义实例索引器(假设 ConfigurationManager 是单例,而不是静态类):

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

    【讨论】:

      【解决方案2】:

      索引器表示法需要引用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"]

      【讨论】:

      • 但是为什么索引器必须使用'this'?它不必访问实例数据
      • +1 表示 Malfist 的评论。仅仅因为它使用“this”作为实例索引器并不意味着他们不能想出其他语法。
      • 同意。你在乞求这个问题。你基本上说它不允许的原因是因为它不被允许。 -1 因为问题是“为什么不允许这样做?”
      • -1 因为此答案假定当前表示法是实现静态索引器时唯一可能的表示法。不一定需要在索引器中使用this,它可能被选择在其他关键字之上,因为它最有意义。对于静态实现,以下语法可能非常可行:public object static[string value]。无需在静态上下文中使用关键字this
      • -1 因为“this”标记被用作关键字,而不是“this”对象的引用。扩展方法非常乐意使用“this”作为关键字,同时保持静态。
      【解决方案3】:

      我相信它被认为不是非常有用。我认为这也很可惜——我倾向于使用的一个例子是编码,其中Encoding.GetEncoding("foo") 可能是Encoding["Foo"]。我不认为它会经常出现非常,但除此之外,它只是感觉有点不一致,不可用。

      我必须检查一下,但我怀疑它已经在 IL(中间语言)中可用。

      【讨论】:

      • 中间语言 - .NET 的一种汇编语言。
      • FWIW,将 IL 中的 instance 更改为 static 以获取默认属性的属性和 getter 方法,导致 ilasm 抱怨 syntax error at token 'static';我不擅长干预 IL 的事务,但这听起来至少是一个初步的不。
      • 我不敢相信有多少次我希望我可以在静态类和非静态类中使用静态索引器(主要是单例,因为我倾向于将 Instance 设为非公共属性)。跨度>
      【解决方案4】:

      this 关键字引用类的当前实例。静态成员函数没有 this 指针。 this 关键字可用于从构造函数、实例方法和实例访问器中访问成员。(从msdn 检索)。由于 this 引用了类的实例,因此它与 static 的性质相冲突,因为 static 不与类的实例相关联。

      一种解决方法如下,它允许您将索引器用于私有 字典,因此您只需要创建一个新实例并访问静态部分。

          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"]
      

      【讨论】:

      • 它是一个有趣的解决方法,但是 1) 它引入了副作用(创建一个空实例对象),这可能会在某些环境中导致内存压力和碎片,2) @987654325 浪费的额外字符@ 本来可以用作单例的限定符名称,例如 .Current
      • 就像Juliet's answer,这并不能回答为什么不支持静态索引器的问题。首先,该问题并未将术语“静态索引器”限制为“使用this 关键字的东西”,其次,语法public string this[int index] 中的this 严格来说甚至没有使用this 指针(因为它可能出现在实例方法的主体中),但只是 token this 的另一种用法。 public static string this[int index] 的语法可能看起来有点违反直觉,但它仍然是明确的。
      • @O.R.Mapper 也可以是public static string class[int index]
      • 我想我很困惑我认为'this关键字是指类的当前实例。静态成员函数没有 this 指针。正在解释由于 this 指针没有引用的对象,因此您不能使用对它的引用。我还说 msdn 是使用该定义的人。据我所知,公共静态字符串与公共字符串永远不会重叠,因为一个访问泛型类型对象,而另一个访问实例对象。
      【解决方案5】:

      原因是很难理解使用静态索引器索引的具体内容。

      您说代码会受益于静态索引器,但真的会这样吗?它所要做的就是改变这一点:

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

      进入这个:

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

      这不会以任何方式使代码变得更好;它并不比许多代码行更小,由于自动完成,编写起来并不容易,而且不太清楚,因为它隐藏了你正在获取和设置你称之为“属性”的东西的事实,它实际上迫使读者请阅读有关索引器返回或设置的确切内容的文档,因为它绝不是您要为其编制索引的属性,而两者都有:

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

      您可以大声朗读并立即理解代码的作用。

      请记住,我们希望编写易于(= 快速)理解的代码,而不是快速编写的代码。不要把你编写代码的速度和你完成项目的速度搞错了。

      【讨论】:

      • 不同意。这只是一个概念点。在我看来,代码确实看起来更好,尽管这只是我的看法。
      • 需要/想要创建一个静态索引器不一定是为了创建“更快编写”的代码,至少在我目前的情况下,它真的不会,在我的情况下,因为我是本质上是创建一个静态 2D 地图,并且所有相关属性(即宽度和高度)已经存在,因此我的地图也将有一个索引器(我可以提供对地图的直接访问,但设置地图在内部完成)
      【解决方案6】:

      使用 C# 6 中较新的构造,您可以使用属性表达式主体来简化单例模式。 例如,我使用了以下与 code-lense 配合得很好的快捷方式:

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

      它还有一个额外的好处是可以查找替换以升级旧代码和统一您的应用程序设置访问。

      【讨论】:

        【解决方案7】:

        我还需要(嗯,更像是不错的)一个静态索引器来存储属性,所以我想出了一个有点尴尬的解决方法:

        在你想拥有一个静态索引器的类中(这里是: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;
        

        但是,唉,如果要实际使用对象作为“值”-Type,那么下面的内容会更短(至少作为声明),并且还提供即时类型转换:

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

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2019-10-11
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2010-09-14
          • 1970-01-01
          • 2013-03-07
          • 1970-01-01
          相关资源
          最近更新 更多