静态索引器?


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

Answers:


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

80
为Malfist的评论+1。仅仅因为它对实例索引器使用“ this”并不意味着他们无法提出其他语法。
乔恩·斯基特

40
同意 你在问这个问题。您基本上已经说过不允许的原因是因为不允许。-1,因为问题是“为什么不允许?”
xr280xr 2012年

15
@ xr280xr +1对于“请求问题”的正确使用:)另外,我也有同样的抱怨。
RedFilter 2014年

14
-1,因为此答案假定当前符号是实现静态索引器的唯一可能符号。this不一定需要在索引器中使用,因为它最有意义,因此它可能比其他关键字优先选择。对于静态的实现,下面的语法可能是相当可行的:public object static[string value]。无需this在静态上下文中使用关键字。
einsteinsci 2015年

91

我认为它不是非常有用。我也觉得很遗憾-我倾向于使用的示例是Encoding,Encoding.GetEncoding("foo")可能在哪里Encoding["Foo"]。我不认为这会拿出十分频繁,但除了别的它只是感觉有点不一致并不可用。

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


6
中间语言-一种.NET的汇编语言。
乔恩·斯基特

14
让我来到这里的是我有一个自定义类,该类通过静态属性公开了整个应用程序中使用的通用值字典。我希望使用静态索引器来缩短从GlobalState.State [KeyName]到GlobalState [KeyName]的访问。本来不错。
xr280xr 2012年

1
FWIW,将属性的IL 更改instancestatic默认属性,将getter方法更改为默认属性会导致ilasm抱怨syntax error at token 'static';我不太擅长干预IL事务,但这听起来至少是最初的否定。
2015年

8

解决方法是,可以在单例/静态对象上定义实例索引器(例如,ConfigurationManager是单例,而不是静态类):

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

1

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

在您要具有静态索引器的类(此处为:Element)内,创建一个同名+“ Dict”的子类。给它一个只读的static作为所述子类的实例,然后添加所需的索引器。

最后,将类添加为静态导入(因此子类仅显示静态字段)。

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;

但是,a,如果要实际使用对象作为“值” -Type,那么下面的内容将更短(至少作为声明),并且还提供立即的Typecasting:

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与类的实例没有关联。

下面是一种解决方法,它允许您对私有Dictionary使用索引器,因此您只需创建一个新实例并访问静态部分。

    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指针(因为它可能出现在实例方法的主体中) ,但这只是令牌的 另一种用途this。该语法public static string this[int index]可能看起来有点违反直觉,但仍然没有歧义。
OR Mapper 2014年

2
@ORMapper也可能是public static string class[int index]
吉姆·

我想我很困惑,我想:“ this关键字引用了该类的当前实例。静态成员函数没有this指针。当时正在解释,因为没有指向此指针引用的对象,所以您不能使用对其的引用。我还说过msdn是使用该定义的人。就我所知,public静态字符串与public字符串绝不会重叠,原因是一个访问通用类型对象,而另一个访问实例对象。
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 '18
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.