Bộ chỉ mục tĩnh?


119

Tại sao các chỉ mục tĩnh không được phép trong C #? Tôi không thấy lý do gì mà chúng không được phép sử dụng và hơn nữa chúng có thể rất hữu ích.

Ví dụ:

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

Đoạn mã trên sẽ được hưởng lợi rất nhiều từ một trình chỉ mục tĩnh. Tuy nhiên, nó sẽ không biên dịch vì không cho phép các chỉ mục tĩnh. Tại sao cái này rất?


Tiếp theo, tôi muốn thực hiện IEnumerable trực tiếp trên lớp tĩnh, vì vậy tôi có thể làm foreach (var enum in Enum):)
nawfal

Câu trả lời:


72

Ký hiệu trình lập chỉ mục yêu cầu tham chiếu tới this. Vì các phương thức tĩnh không có tham chiếu đến bất kỳ phiên bản cụ thể nào của lớp, bạn không thể sử dụng thischúng và do đó bạn không thể sử dụng ký hiệu trình chỉ mục trên các phương thức tĩnh.

Giải pháp cho vấn đề của bạn là sử dụng một mẫu singleton như sau:

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

Bây giờ bạn có thể gọi Utilities.ConfigurationManager["someKey"]bằng ký hiệu trình lập chỉ mục.


110
Nhưng tại sao người lập chỉ mục phải sử dụng 'this'? Nó không phải dữ liệu Ví dụ truy cập
Malfist

80
+1 cho nhận xét của Malfist. Chỉ vì nó sử dụng "this" cho một trình chỉ mục cá thể không có nghĩa là họ không thể nghĩ ra cú pháp khác.
Jon Skeet

40
Đã đồng ý. Bạn đang cầu xin câu hỏi. Về cơ bản, bạn đã nói lý do nó không được phép là vì nó không được phép. -1 vì câu hỏi là "tại sao nó không được phép?"
xr280xr

15
@ xr280xr +1 Để sử dụng chính xác "cầu xin câu hỏi" :) Thêm vào đó, tôi cũng có khiếu nại tương tự.
RedFilter

14
-1 vì câu trả lời này giả định rằng ký hiệu hiện tại là ký hiệu khả thi duy nhất nếu một trình chỉ mục tĩnh được triển khai. Việc sử dụng thistrong một trình chỉ mục không nhất thiết phải bắt buộc, nó có thể được chọn bên trên các từ khóa khác vì nó có ý nghĩa nhất. Đối với việc thực hiện tĩnh, cú pháp sau đây có thể khá hữu hiệu: public object static[string value]. Không cần sử dụng từ khóa thistrong ngữ cảnh tĩnh.
einsteinsci

91

Tôi tin rằng nó được coi là không hữu ích khủng khiếp. Tôi nghĩ cũng thật đáng tiếc - một ví dụ mà tôi có xu hướng sử dụng là Mã hóa, nếu Encoding.GetEncoding("foo")có thể Encoding["Foo"]. Tôi không nghĩ rằng nó sẽ đưa ra rất thường xuyên, nhưng một số tồn tại bất cứ thứ gì nó chỉ cảm thấy một chút mâu thuẫn không có sẵn.

Tôi sẽ phải kiểm tra, nhưng tôi nghi ngờ nó đã có sẵn trong IL (Ngôn ngữ Trung cấp) rồi.


6
Ngôn ngữ trung gian - loại hợp ngữ cho .NET.
Jon Skeet

15
Điều đưa tôi đến đây là tôi có một lớp tùy chỉnh hiển thị từ điển các giá trị chung được sử dụng trong ứng dụng của tôi thông qua thuộc tính tĩnh. Tôi đã hy vọng sử dụng trình lập chỉ mục tĩnh để rút ngắn quyền truy cập từ GlobalState.State [KeyName] xuống chỉ GlobalState [KeyName]. Sẽ rất tuyệt.
xr280xr

1
FWIW, thay đổi instanceđể statictrong IL cho một tài sản và getter phương pháp trên một kết quả bất động sản mặc định trong ILASM than phiền syntax error at token 'static'; Tôi không giỏi can thiệp vào các công việc của IL nhưng điều đó có vẻ ít nhất là không.
Amazingant

8

Khi làm việc xung quanh, bạn có thể xác định một trình chỉ mục cá thể trên một đối tượng singleton / static (giả sử rằng ConfigurationManager là một singleton, thay vì là một lớp tĩnh):

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

1

Tôi cũng cần (tốt hơn là cần có) một trình chỉ mục tĩnh để lưu trữ các thuộc tính, vì vậy tôi đã tìm ra một giải pháp hơi khó xử:

Trong lớp bạn muốn có một chỉ mục tĩnh (ở đây: Phần tử), hãy tạo một lớp con có cùng tên + "Dict". Cung cấp cho nó một chỉ đọc tĩnh như trường hợp của lớp con đã nói, và sau đó thêm trình chỉ mục mong muốn của bạn.

Cuối cùng, thêm lớp dưới dạng nhập tĩnh (do đó lớp con chỉ hiển thị trường tĩnh).

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

và sau đó bạn có thể sử dụng nó được viết hoa dưới dạng Loại hoặc không được viết hoa dưới dạng từ điển:

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

Nhưng than ôi, nếu người ta thực sự sử dụng đối tượng làm "giá trị" -Type, thì phần dưới đây sẽ vẫn ngắn hơn (ít nhất là khi khai báo) và cũng cung cấp tính năng Đánh máy ngay lập tức:

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

Với các cấu trúc mới hơn trong C # 6, bạn có thể đơn giản hóa mẫu singleton với phần thân biểu thức thuộc tính. Ví dụ: tôi đã sử dụng phím tắt sau đây hoạt động tốt với code-lense:

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

Nó có thêm lợi ích là có thể tìm thấy thay thế để nâng cấp mã cũ hơn và thống nhất quyền truy cập cài đặt ứng dụng của bạn.


-2

Từ khóa this đề cập đến phiên bản hiện tại của lớp. Hàm thành viên tĩnh không có con trỏ này. Từ khóa this có thể được sử dụng để truy cập các thành viên từ bên trong các hàm tạo, các phương thức cá thể và các trình truy cập cá thể. (Truy xuất từ msdn ). Vì nó tham chiếu đến một thể hiện của lớp nên nó xung đột với bản chất của static, vì static không được liên kết với một thể hiện của lớp.

Một cách giải quyết sẽ là sau đây cho phép bạn sử dụng trình lập chỉ mục đối với Từ điển riêng, do đó bạn chỉ cần tạo một phiên bản mới và bạn truy cập vào phần tĩnh.

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

Điều này cho phép bạn bỏ qua toàn bộ việc truy cập vào một thành viên của lớp và chỉ cần tạo một thể hiện của nó và lập chỉ mục nó.

    new ConfigurationManager()["ItemName"]

4
đó là một cách giải quyết thú vị, nhưng 1) nó đưa ra các hiệu ứng phụ (tạo một đối tượng thể hiện trống) có thể dẫn đến áp lực bộ nhớ và phân mảnh trong một số môi trường, 2) các ký tự thừa bị lãng phí new ()có thể đã được sử dụng cho tên định tính của một singleton thay vào đó, như.Current
Lawrence Ward

1
Cũng giống như câu trả lời của Juliet , điều này không trả lời câu hỏi tại sao các trình chỉ mục tĩnh không được hỗ trợ. Đầu tiên, câu hỏi không giới hạn thuật ngữ "static indexer" thành "thứ gì đó sử dụng thistừ khóa" và thứ hai, thistrong cú pháp, public string this[int index]nói đúng ra là thậm chí không sử dụng một thiscon trỏ (vì nó có thể xảy ra trong phần thân của các phương thức phiên bản) , nhưng chỉ là một công dụng khác của mã thông báo this . Cú pháp public static string this[int index]có thể tìm kiếm một phản trực giác chút, nhưng nó vẫn sẽ là rõ ràng.
HOẶC Người vẽ bản đồ

2
@ORMapper Nó cũng có thể là như vậy public static string class[int index].
Jim Balter,

Tôi đoán tôi đang bối rối, tôi nghĩ 'Từ khóa này đề cập đến phiên bản hiện tại của lớp. Hàm thành viên tĩnh không có con trỏ này. ' giải thích rằng vì không có đối tượng nào để con trỏ này tham chiếu, bạn không thể sử dụng tham chiếu đến nó. Và tôi cũng tuyên bố msdn là người sử dụng định nghĩa đó. public static string so với public string sẽ không bao giờ trùng lặp theo hiểu biết của tôi do thực tế là một người truy cập vào đối tượng kiểu chung, trong khi người kia sẽ truy cập một đối tượng cá thể.
lamorach

-2

Lý do là vì khá khó để hiểu chính xác bạn đang lập chỉ mục bằng trình chỉ mục tĩnh.

Bạn nói rằng mã sẽ được hưởng lợi từ một trình chỉ mục tĩnh, nhưng nó có thực sự không? Tất cả những gì nó sẽ làm là thay đổi điều này:

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

Vào cái này:

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

điều này không làm cho mã tốt hơn theo bất kỳ cách nào; nó không nhỏ hơn bởi nhiều dòng mã, nó không dễ viết hơn nhờ tính năng tự động hoàn thành và nó kém rõ ràng hơn, vì nó che giấu sự thật rằng bạn đang nhận và thiết lập thứ mà bạn gọi là 'Thuộc tính' và nó thực sự buộc người đọc phải hãy đọc tài liệu về những gì chính xác mà trình lập chỉ mục trả về hoặc đặt, bởi vì không có cách nào rõ ràng rằng nó là một thuộc tính mà bạn đang lập chỉ mục, trong khi với cả hai:

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

Bạn có thể đọc to và hiểu ngay mã hoạt động.

Hãy nhớ rằng chúng ta muốn viết mã dễ hiểu (= nhanh), không phải mã viết nhanh. Đừng nhầm giữa tốc độ bạn có thể viết mã với tốc độ bạn hoàn thành dự án.


8
Không đồng ý. Đó chỉ là một điểm khái niệm. Và mã trông đẹp hơn theo quan điểm của tôi, mặc dù đó chỉ là ý kiến ​​của tôi.
ouflak
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.