ดัชนีคงที่?


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"]โดยใช้สัญกรณ์ Indexer


110
แต่ทำไม Indexer ต้องใช้ 'this'? ไม่ต้องเข้าถึงข้อมูลอินสแตนซ์
Malfist

80
+1 สำหรับความคิดเห็นของ Malfist เพียงเพราะใช้ "this" สำหรับตัวทำดัชนีอินสแตนซ์ไม่ได้หมายความว่าพวกเขาไม่สามารถสร้างไวยากรณ์อื่น
Jon Skeet

40
ตกลง คุณกำลังขอร้องคำถาม โดยพื้นฐานแล้วคุณได้บอกว่าสาเหตุที่ไม่อนุญาตนั้นเป็นเพราะไม่อนุญาต -1 เนื่องจากคำถามคือ "ทำไมจึงไม่อนุญาต"
xr280xr

15
@ xr280xr +1 สำหรับการใช้ "ขอทานคำถาม" อย่างถูกต้อง :) นอกจากนี้ฉันมีข้อร้องเรียนเดียวกัน
RedFilter

14
-1 เนื่องจากคำตอบนี้ถือว่าสัญกรณ์ปัจจุบันเป็นสัญกรณ์เดียวที่เป็นไปได้หากใช้ตัวทำดัชนีแบบคงที่ thisไม่จำเป็นต้องใช้ในตัวสร้างดัชนี แต่มีแนวโน้มว่าจะถูกเลือกไว้เหนือคำหลักอื่น ๆ เพราะเหมาะสมที่สุด public object static[string value]สำหรับการดำเนินงานคงไวยากรณ์ต่อไปนี้อาจจะทำงานได้มาก: ไม่จำเป็นต้องใช้คำหลักthisในบริบทคงที่
einsteinsci

91

ฉันเชื่อว่ามันไม่ได้มีประโยชน์อย่างยิ่ง ฉันคิดว่ามันก็น่าเสียดายเช่นกัน - ตัวอย่างที่ฉันมักจะใช้คือการเข้ารหัสซึ่งEncoding.GetEncoding("foo")อาจเป็นEncoding["Foo"]ได้ ผมไม่คิดว่ามันจะเกิดขึ้นมากบ่อย แต่นอกเหนือจากสิ่งอื่นมันเป็นเพียงแค่ความรู้สึกที่ไม่สอดคล้องกันเล็ก ๆ น้อย ๆ ที่จะไม่สามารถใช้ได้

ฉันจะต้องตรวจสอบ แต่ฉันสงสัยว่ามันมีอยู่ใน IL (ภาษากลาง) แล้ว


6
Intermediate Language - ประเภทของภาษาแอสเซมบลีสำหรับ. NET
Jon Skeet

15
สิ่งที่ทำให้ฉันมาที่นี่คือฉันมีคลาสแบบกำหนดเองที่แสดงพจนานุกรมของค่าทั่วไปที่ใช้ในแอปพลิเคชันของฉันผ่านคุณสมบัติคงที่ ฉันหวังว่าจะใช้ตัวสร้างดัชนีแบบคงที่เพื่อลดการเข้าถึงจาก GlobalState.State [KeyName] ให้เหลือแค่ GlobalState [KeyName] คงจะดีไม่น้อย
xr280xr

1
FWIW เปลี่ยนinstanceไปstaticใน IL หาวิธีการที่ทรัพย์สินและทะเยอทะยานในผลการค้นหาสถานที่ให้บริการเริ่มต้นใน ilasm บ่นsyntax error at token 'static'; ฉันไม่เก่งในการเข้าไปยุ่งเกี่ยวกับกิจการของ IL แต่ดูเหมือนว่าอย่างน้อยก็เป็นเลขเริ่มต้น
Amazingant

8

ในการแก้ไขปัญหาคุณสามารถกำหนดตัวสร้างดัชนีอินสแตนซ์บนอ็อบเจ็กต์ซิงเกิลตัน / สแตติก (กล่าวว่า ConfigurationManager เป็นซิงเกิลตันแทนที่จะเป็นคลาสแบบคงที่):

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

1

ฉันยังต้องการตัวสร้างดัชนีแบบคงที่ (เหมือนดีมาก) เพื่อจัดเก็บแอตทริบิวต์ดังนั้นฉันจึงคิดหาวิธีแก้ปัญหาที่ค่อนข้างอึดอัด:

ภายในคลาสคุณต้องการมีตัวสร้างดัชนีแบบคงที่ (ที่นี่: องค์ประกอบ) ให้สร้างคลาสย่อยที่มีชื่อเดียวกัน + "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;

แต่อนิจจาหากมีการใช้ object เป็น "value" -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);

0

ด้วยโครงสร้างที่ใหม่กว่าใน C # 6 คุณอาจลดความซับซ้อนของรูปแบบซิงเกิลตันด้วยเนื้อหานิพจน์คุณสมบัติ ตัวอย่างเช่นฉันใช้ทางลัดต่อไปนี้ซึ่งทำงานได้ดีกับเลนส์โค้ด:

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

มีประโยชน์เพิ่มเติมในการค้นหา - แทนที่ได้สำหรับการอัปเกรดโค้ดรุ่นเก่าและรวมการเข้าถึงการตั้งค่าแอปพลิเคชันของคุณ


-2

คีย์เวิร์ดนี้อ้างถึงอินสแตนซ์ปัจจุบันของคลาส ฟังก์ชันสมาชิกคงไม่มีตัวชี้นี้ คำหลักนี้สามารถใช้เพื่อเข้าถึงสมาชิกจากภายในตัวสร้างวิธีการของอินสแตนซ์และตัวเข้าถึงอินสแตนซ์ (ดึงมาจากmsdn ) เนื่องจากสิ่งนี้อ้างอิงอินสแตนซ์ของคลาสจึงขัดแย้งกับธรรมชาติของสแตติกเนื่องจากสแตติกไม่เกี่ยวข้องกับอินสแตนซ์ของคลาส

วิธีแก้ปัญหาอย่างหนึ่งคือวิธีต่อไปนี้ซึ่งช่วยให้คุณสามารถใช้ตัวทำดัชนีกับพจนานุกรมส่วนตัวได้ดังนั้นคุณจะต้องสร้างอินสแตนซ์ใหม่และคุณเข้าถึงส่วนที่คงที่

    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
Lawrence Ward

1
เช่นเดียวกับคำตอบของ Julietสิ่งนี้ไม่ได้ตอบคำถามว่าทำไมตัวทำดัชนีแบบคงที่จึงไม่รองรับ ประการแรกคำถามไม่ได้ จำกัด คำว่า "ตัวทำดัชนีแบบคงที่" เป็น "สิ่งที่ใช้thisคีย์เวิร์ด" และประการที่สองthisในไวยากรณ์public string this[int index]นั้นพูดอย่างเคร่งครัดไม่เว้นแม้แต่การใช้thisตัวชี้ (เนื่องจากอาจเกิดขึ้นในเนื้อความของวิธีการอินสแตนซ์) แต่เป็นเพียงการใช้โทเค็น thisอื่น ไวยากรณ์public static string this[int index]อาจดูขัดกันเล็กน้อย แต่ก็ยังไม่คลุมเครือ
หรือผู้ทำแผนที่

2
@ORMapper public static string class[int index]มันอาจก็เช่นกันจะ
Jim Balter

ฉันเดาว่าฉันสับสนฉันคิดว่า 'คีย์เวิร์ดนี้หมายถึงอินสแตนซ์ปัจจุบันของคลาส ฟังก์ชันสมาชิกคงไม่มีตัวชี้นี้ ' กำลังอธิบายว่าเนื่องจากไม่มีวัตถุใดให้ตัวชี้นี้อ้างอิงคุณจึงไม่สามารถใช้การอ้างอิงได้ และฉันยังระบุว่า 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
ไม่เห็นด้วย นั่นเป็นเพียงประเด็นทางความคิด และรหัสดูดีกว่าในความคิดของฉันแม้ว่านั่นจะเป็นเพียงความคิดของฉันก็ตาม
camouflageak
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.