क्यों C # अनुक्रमित संपत्तियों को लागू नहीं करता है?


83

मुझे पता है, मुझे पता है ... इस तरह के सवाल का एरिक लिपर्ट का जवाब आमतौर पर "जैसा कुछ होता है क्योंकि यह डिजाइन, कार्यान्वयन, परीक्षण और दस्तावेज बनाने की लागत के लायक नहीं था "।

लेकिन फिर भी, मैं एक बेहतर स्पष्टीकरण चाहता हूं ... मैं इस ब्लॉग पोस्ट को नई सी # 4 विशेषताओं के बारे में पढ़ रहा था , और COM इंटरॉप के बारे में अनुभाग में, निम्नलिखित भाग ने मेरा ध्यान आकर्षित किया:

वैसे, यह कोड एक और नई सुविधा का उपयोग करता है: अनुक्रमित गुण (श्रेणी के बाद उन चौकोर कोष्ठक पर एक करीब से नज़र डालें।) लेकिन यह सुविधा केवल COM इंटरॉप के लिए उपलब्ध है; आप C # 4.0 में अपने स्वयं के अनुक्रमित गुण नहीं बना सकते

ठीक है, लेकिन क्यों ? मुझे पहले से ही पता था और पछतावा था कि C # में अनुक्रमित गुण बनाना संभव नहीं था, लेकिन इस वाक्य ने मुझे इसके बारे में फिर से सोचने पर मजबूर कर दिया। मैं इसे लागू करने के कई अच्छे कारण देख सकता हूं:

  • CLR इसका समर्थन करता है (उदाहरण के लिए, PropertyInfo.GetValueएक indexपैरामीटर है), इसलिए यह अफ़सोस की बात है कि हम C # में इसका लाभ नहीं उठा सकते हैं
  • यह COM इंटरॉप के लिए समर्थित है, जैसा कि लेख में दिखाया गया है (गतिशील प्रेषण का उपयोग करके)
  • इसे VB.NET में लागू किया गया है
  • इंडेक्सर्स बनाने के लिए पहले से ही, यानी इंडेक्स को ऑब्जेक्ट पर लागू करना संभव है, इसलिए यह विचार को गुणों में विस्तारित करने के लिए कोई बड़ा सौदा नहीं होगा, समान सिंटैक्स रखते हुए और केवल thisएक संपत्ति के नाम के साथ प्रतिस्थापित करना

यह उस तरह की चीजों को लिखने की अनुमति देगा:

public class Foo
{
    private string[] _values = new string[3];
    public string Values[int index]
    {
        get { return _values[index]; }
        set { _values[index] = value; }
    }
}

वर्तमान में एकमात्र वर्कअराउंड जो मुझे पता है कि एक आंतरिक वर्ग ( ValuesCollectionउदाहरण के लिए) बनाना है जो एक इंडेक्सर को लागू करता है, और Valuesसंपत्ति को बदलता है ताकि यह उस आंतरिक वर्ग का एक उदाहरण लौटाए।

यह करना बहुत आसान है, लेकिन कष्टप्रद ... तो शायद कंपाइलर हमारे लिए यह कर सकता है! एक विकल्प एक आंतरिक वर्ग उत्पन्न करना होगा जो सूचकांक को लागू करता है, और इसे एक सार्वजनिक जेनेरिक इंटरफ़ेस के माध्यम से उजागर करता है:

// interface defined in the namespace System
public interface IIndexer<TIndex, TValue>
{
    TValue this[TIndex index]  { get; set; }
}

public class Foo
{
    private string[] _values = new string[3];

    private class <>c__DisplayClass1 : IIndexer<int, string>
    {
        private Foo _foo;
        public <>c__DisplayClass1(Foo foo)
        {
            _foo = foo;
        }

        public string this[int index]
        {
            get { return _foo._values[index]; }
            set { _foo._values[index] = value; }
        }
    }

    private IIndexer<int, string> <>f__valuesIndexer;
    public IIndexer<int, string> Values
    {
        get
        {
            if (<>f__valuesIndexer == null)
                <>f__valuesIndexer = new <>c__DisplayClass1(this);
            return <>f__valuesIndexer;
        }
    }
}

लेकिन निश्चित रूप से, उस मामले में संपत्ति वास्तव में एक लौटेगी IIndexer<int, string>, और वास्तव में एक अनुक्रमित संपत्ति नहीं होगी ... एक वास्तविक सीएलआर अनुक्रमित संपत्ति उत्पन्न करना बेहतर होगा।

तुम क्या सोचते हो ? क्या आप इस सुविधा को C # में देखना चाहेंगे? यदि नहीं, तो क्यों?


1
मुझे लगता है कि यह उन में से एक है "हमें एक्स के लिए अनुरोध मिलते हैं लेकिन वाई के मुद्दों से अधिक नहीं"
चाओसपांडियन

1
@ChaosPandion, हाँ, आप शायद सही हैं ... लेकिन इस सुविधा को लागू करना बहुत आसान होगा, और हालांकि यह निश्चित रूप से "नहीं" होना चाहिए, यह निश्चित रूप से "अच्छा है" श्रेणी में आता है
थॉमस लेवेस्क

4
इंडेक्स सीएलआर दृष्टिकोण से पहले से ही थोड़ा परेशान हैं। वे कोड के लिए एक नया सीमा मामला जोड़ते हैं जो गुणों के साथ काम करना चाहते हैं, क्योंकि अब कोई भी संपत्ति अनुक्रमणिका पैरामीटर हो सकती है। मुझे लगता है कि C # कार्यान्वयन समझ में आता है, क्योंकि एक इंडेक्सर आमतौर पर जिस वस्तु का प्रतिनिधित्व करता है वह किसी वस्तु की संपत्ति नहीं है, बल्कि इसकी 'सामग्री' है। यदि आप मनमाने अनुक्रमणिका गुण प्रदान करते हैं, तो आप लगा रहे हैं कि कक्षा में सामग्री के विभिन्न समूह हो सकते हैं, जो स्वाभाविक रूप से जटिल उप-सामग्री को एक नए वर्ग के रूप में संलग्न करने की ओर ले जाता है। मेरा सवाल है: सीएलआर अनुक्रमित गुण क्यों प्रदान करता है?
डैन ब्रायंट

1
@tk_ आपकी रचनात्मक टिप्पणी के लिए धन्यवाद। क्या आप सभी भाषाओं के बारे में ऐसी ही टिप्पणियाँ पोस्ट कर रहे हैं जो फ्री पास्कल नहीं हैं? खैर, मुझे आशा है कि यह आपको खुद के बारे में अच्छा महसूस
कराएगा

3
यह उन कुछ स्थितियों में से एक है जहां C ++ / CLI और VB.net C # से बेहतर हैं। मैंने अपने C ++ / CLI कोड में बहुत सी अनुक्रमित संपत्तियों को लागू किया है और अब इसे C # में परिवर्तित करने पर मुझे उन सभी के लिए वर्कअराउंड ढूंढना होगा। :-( SUCKS !!! // आपका यह लिखने की अनुमति होगी कि इस तरह की चीजें जो मैंने वर्षों में की हैं।
टोबियास नोज़

जवाबों:


122

यहां हमने C # 4 को कैसे डिज़ाइन किया है।

पहले हमने हर संभव सुविधा की एक सूची बनाई जिसे हम भाषा में जोड़ने के बारे में सोच सकते थे।

फिर हमने सुविधाओं को "यह बुरा है, हमें इसे कभी नहीं करना चाहिए" में बदल दिया, "यह बहुत बढ़िया है, हमें इसे करना है", और "यह अच्छा है लेकिन चलो इस बार ऐसा नहीं करते हैं"।

फिर हमने देखा कि हमें कितना बजट डिजाइन, कार्यान्वित, परीक्षण, दस्तावेज़, जहाज और "गॉट्टा है" सुविधाओं को बनाए रखना था और पता चला कि हम बजट में 100% थे।

इसलिए हम "बाल्टी है" बाल्टी से "अच्छा है" बाल्टी के लिए सामान का एक गुच्छा ले जाया गया।

अनुक्रमित गुण "गॉट्टा है" सूची के शीर्ष के पास कहीं भी नहीं थे । वे "अच्छी" सूची में बहुत कम हैं और "बुरे विचार" की सूची के साथ छेड़खानी कर रहे हैं।

हर मिनट हम अच्छी सुविधा X को डिजाइन करने, कार्यान्वित करने, परीक्षण, दस्तावेजीकरण या बनाए रखने में बिताते हैं, एक ऐसा मिनट है जिसे हम भयानक विशेषताओं ए, बी, सी, डी, ई, एफ और जी पर खर्च नहीं कर सकते हैं। हमें निर्ममता से प्राथमिकता देना होगा ताकि हम केवल सर्वोत्तम संभव सुविधाएँ करें। अनुक्रमित गुण अच्छा होगा, लेकिन वास्तव में लागू करने के लिए अच्छा कहीं भी अच्छा नहीं है।


20
क्या मैं इसे खराब सूची में डालने के लिए वोट जोड़ सकता हूं? मैं वास्तव में यह नहीं देखता कि मौजूदा क्रियान्वयन कितना सीमित है, जब आप एक नेस्टेड प्रकार को उजागर कर सकते हैं जो एक अनुक्रमणिका को लागू करता है। मैं कल्पना करूँगा कि आप बहुत सी हैकरी को देखना शुरू कर देंगे, ताकि डेटाबाइंडिंग और प्रॉपर्टीज़ में कुछ करने की कोशिश की जा सके।
जोश

11
और उम्मीद है कि ऑटो-इंप्रूव्ड INotifyPropertyChanged इंडेक्स की गई संपत्तियों की तुलना में सूची में अधिक है। :)
जोश

3
@ एरिक, ठीक है, यही मुझे संदेह है ... वैसे भी जवाब देने के लिए धन्यवाद! मुझे लगता है कि मैं अनुक्रमित गुणों के बिना रह सकता हूं, जैसा कि मैंने वर्षों से किया है;)
थॉमस लेवेस्क ने

5
@ मर्टिन: मैं इस बात का विशेषज्ञ नहीं हूं कि बड़ी सॉफ्टवेयर टीमें बजट कैसे निर्धारित की जाती हैं। आपके प्रश्न को सोमा, जेसन ज़ेंडर या स्कॉट विल्टमथ को संबोधित किया जाना चाहिए, मेरा मानना ​​है कि सभी कभी-कभार ब्लॉग लिखते हैं। स्काला से आपकी तुलना एक सेब-से-संतरे की तुलना है; स्काला में अधिकांश लागत नहीं है जो C # करता है; सिर्फ एक का नाम रखने के लिए, इसमें लाखों उपयोगकर्ता नहीं हैं जिनके पास अत्यंत महत्वपूर्ण पश्चगामी संगतता आवश्यकताएं हैं। मैं आपको कई और कारकों का नाम दे सकता हूं जो C # और स्काला के बीच बड़े पैमाने पर लागत अंतर पैदा कर सकते हैं।
एरिक लिपर्ट

12
+1: लोग इसे पढ़कर सॉफ़्टवेयर प्रोजेक्ट प्रबंधित करने के बारे में बहुत कुछ सीख सकते हैं। और यह केवल कुछ पंक्तियाँ हैं।
ब्रायन मैकके

22

एसी # इंडेक्सर है एक अनुक्रमित संपत्ति। इसे Itemडिफ़ॉल्ट रूप से नाम दिया गया है (और आप इसे उदाहरण के लिए VB से संदर्भित कर सकते हैं), और यदि आप चाहें तो इसे IndexerNameAttribute के साथ बदल सकते हैं।

मुझे यकीन नहीं है कि क्यों, विशेष रूप से, यह उस तरह से डिज़ाइन किया गया था, लेकिन यह एक जानबूझकर सीमा लगती है। हालाँकि, यह फ्रेमवर्क डिज़ाइन दिशानिर्देशों के अनुरूप है, जो सदस्य संग्रह के लिए एक अनुक्रमित वस्तु को लौटाने वाली गैर-अनुक्रमित संपत्ति के दृष्टिकोण की सिफारिश करते हैं। यानी "इंडेक्सेबल होना" एक प्रकार का लक्षण है; यदि यह एक से अधिक तरीकों से अनुक्रमित है, तो इसे वास्तव में कई प्रकारों में विभाजित किया जाना चाहिए।


1
धन्यवाद! जब मैं एक डिफ़ॉल्ट indxer (DISPID 0) है जो इस [int] के रूप में आयात करता है, जिसका नाम मूल रूप से "आइटम" (कभी-कभी यह "आइटम" या "मान", या समान नहीं है) को लागू करने के दौरान मैंने बार-बार त्रुटियों से लड़ाई लड़ी है )। यह संकलन करता है और वैसे भी चलता है, लेकिन FxCop CA1033 InterfaceMethodsShouldBeCallableByChildTypes चेतावनियाँ, सीएलएस-अनुपालन मुद्दे (केवल मामले में अलग पहचानकर्ता), आदि, क्योंकि नाम काफी फिट नहीं है। [IndexerName] वह सब है जिसकी आवश्यकता थी, लेकिन मैं इसे खोजने में कभी कामयाब नहीं हुआ।
puetzk

धन्यवाद!!! IndexerName विशेषता ने मुझे MSIL हस्ताक्षरों को तोड़े बिना एक VB असेंबली को C # में बदलने की अनुमति दी।
बजे जॉन तिर्जन

15

क्योंकि आप पहले से ही इसे कर सकते हैं, और यह आपको OO पहलुओं पर सोचने के लिए मजबूर कर रहा है, अनुक्रमित गुणों को जोड़ने से भाषा पर अधिक शोर होगा। और दूसरा काम करने का सिर्फ एक तरीका।

class Foo
{
    public Values Values { ... }
}

class Values
{
    public string this[int index] { ... }    
}

foo.Values[0]

मैं व्यक्तिगत रूप से केवल 10 तरीकों के बजाय कुछ करने का एक ही तरीका देखना पसंद करूंगा। लेकिन निश्चित रूप से यह एक व्यक्तिपरक राय है।


2
+1, यह VB5 निर्माणों के साथ भाषा को गम करने की तुलना में इसे लागू करने का एक बेहतर तरीका है।
जोश

1
+1 क्योंकि यह है कि मैं इसे कैसे करूंगा। और अगर तुम अच्छे होते तो तुम शायद यह सामान्य बना पाते।
टोनी

10
उस दृष्टिकोण के साथ एक समस्या यह है कि इंडेक्स की एक प्रति बनाने के लिए अन्य कोड के लिए संभव है, और यह स्पष्ट नहीं है कि यदि यह होता है तो शब्दार्थ क्या होना चाहिए। अगर कोड कहता है "var userList = Foo.Users; Foo.RemoveSomeUsers (); someUser = userList [5];" होना चाहिए कि क्या Foo (RemoveSomeUsers से पहले) के तत्व, [5] या उसके बाद उपयोग किया जाता है? यदि एक उपयोगकर्ता सूची [] एक अनुक्रमित संपत्ति थी, तो इसे सीधे उजागर नहीं करना होगा।
सुपरकाट

क्या आपको ग्राहक आवंटित करना पसंद है?
जोशुआ

9

मैं अनुक्रमित गुणों के विचार का पक्ष लेता था, लेकिन तब एहसास हुआ कि इससे भयानक अस्पष्टता और वास्तव में कार्यक्षमता का विघटन होगा । अनुक्रमित गुणों का मतलब होगा कि आपके पास एक बच्चा संग्रह उदाहरण नहीं है। वह अच्छा और बुरा दोनों है। इसे लागू करने के लिए कम परेशानी है और आपको संलग्न मालिक वर्ग के लिए संदर्भ की आवश्यकता नहीं है। लेकिन इसका मतलब यह भी है कि आप उस बच्चे के संग्रह को कुछ भी नहीं दे सकते हैं; आपको हर एक समय की गणना करनी होगी। और न ही आप इस पर फोरचेक कर सकते हैं। सबसे बुरी बात, आप किसी अनुक्रमित संपत्ति को देखने से नहीं बता सकते हैं कि यह संपत्ति है या संग्रह संपत्ति।

विचार तर्कसंगत है लेकिन यह सिर्फ अनम्यता और अचानक अजीबता की ओर जाता है।


दिलचस्प विचार, भले ही जवाब थोड़ा देर हो;)। आप बहुत अच्छे अंक बना रहे हैं, +1।
थॉमस लेवेस्क

Vb.net में, एक वर्ग एक ही नाम [जैसे Bar] की अनुक्रमित और गैर-अनुक्रमित संपत्ति दोनों हो सकता है । अभिव्यक्ति Thing.Bar(5)अनुक्रमित संपत्ति का प्रयोग करेंगे Barपर Thingहै, जबकि (Thing.Bar)(5)गैर-सूचीबद्ध संपत्ति का प्रयोग करेंगे Barऔर फिर जिसके परिणामस्वरूप वस्तु के डिफ़ॉल्ट इंडेक्सर का उपयोग करें। मेरी बाँध करने के लिए, की अनुमति देता है Thing.Bar[5]की संपत्ति होने के लिए Thingकी एक संपत्ति है, बजाय Thing.Bar, है अच्छा है, के बाद से अन्य बातों के अलावा, यह उस समय में कुछ पल में, का अर्थ हो सकता है Thing.Bar[4]स्पष्ट हो सकता है, लेकिन के समुचित प्रभाव ...
सुपरकैट

... ऐसा कुछ var temp=Thing.Bar; do_stuff_with_thing; var q=temp[4]अस्पष्ट हो सकता है। इस धारणा पर भी विचार करें कि Thingडेटा Barको किसी ऐसे क्षेत्र में रखा जा सकता है जो या तो साझा अपरिवर्तनीय वस्तु हो सकता है या कोई अनसेच्ड म्यूटेबल ऑब्जेक्ट; Barबैकिंग फ़ील्ड अपरिवर्तनीय है, यह लिखने का प्रयास एक परस्पर प्रतिलिपि बनाना चाहिए, लेकिन इससे पढ़ने का प्रयास नहीं करना चाहिए। यदि Barएक नामित अनुक्रमित संपत्ति है, तो अनुक्रमित गटर अकेले बैकिंग संग्रह को छोड़ सकता है (चाहे उत्परिवर्तित या नहीं) जबकि सेटर जरूरत पड़ने पर एक नई परिवर्तनशील प्रतिलिपि बना सकता है।
सुपरकैट

एक संपत्ति का एक अनुक्रमणिका एक एन्यूमरेटर नहीं है - यह एक कुंजी है
जॉर्ज बीरबिलिस

6

मुझे साफ, संक्षिप्त कोड लिखने की कोशिश करते समय अनुक्रमित गुणों की कमी बहुत निराशा होती है। अनुक्रमित संपत्ति को एक अलग संदर्भ प्रदान करने की तुलना में एक बहुत अलग अर्थ है जो अनुक्रमित या व्यक्तिगत तरीके प्रदान करता है। मुझे यह थोड़ा परेशान करता है कि एक आंतरिक वस्तु तक पहुंच प्रदान करना जो एक अनुक्रमित संपत्ति को लागू करता है यहां तक ​​कि स्वीकार्य माना जाता है क्योंकि अक्सर ऑब्जेक्ट ओरिएंटेशन के प्रमुख घटकों में से एक को तोड़ता है: इनकैप्सुलेशन।

मैं इस समस्या में अक्सर पर्याप्त रूप से भागता हूं, लेकिन मैंने इसे आज फिर से सामना किया इसलिए मैं एक वास्तविक विश्व कोड उदाहरण प्रदान करूंगा। इंटरफेस और क्लास लिखा जा रहा है जो एप्लिकेशन कॉन्फ़िगरेशन को संग्रहीत करता है जो शिथिल संबंधित जानकारी का एक संग्रह है। मुझे स्क्रिप्ट के टुकड़ों को जोड़ने की जरूरत है और अनाम वर्ग सूचकांक का उपयोग करने से बहुत गलत संदर्भ निहित होगा क्योंकि स्क्रिप्ट के टुकड़े कॉन्फ़िगरेशन का हिस्सा हैं।

यदि अनुक्रमित गुण C # में उपलब्ध थे, तो मैं नीचे दिए गए कोड को लागू कर सकता था (वाक्य रचना यह है [Key] बदलकर PropertyName [कुंजी])।

public interface IConfig
{
    // Other configuration properties removed for examp[le

    /// <summary>
    /// Script fragments
    /// </summary>
    string Scripts[string name] { get; set; }
}

/// <summary>
/// Class to handle loading and saving the application's configuration.
/// </summary>
internal class Config : IConfig, IXmlConfig
{
  #region Application Configuraiton Settings

    // Other configuration properties removed for examp[le

    /// <summary>
    /// Script fragments
    /// </summary>
    public string Scripts[string name]
    {
        get
        {
            if (!string.IsNullOrWhiteSpace(name))
            {
                string script;
                if (_scripts.TryGetValue(name.Trim().ToLower(), out script))
                    return script;
            }
            return string.Empty;
        }
        set
        {
            if (!string.IsNullOrWhiteSpace(name))
            {
                _scripts[name.Trim().ToLower()] = value;
                OnAppConfigChanged();
            }
        }
    }
    private readonly Dictionary<string, string> _scripts = new Dictionary<string, string>();

  #endregion

    /// <summary>
    /// Clears configuration settings, but does not clear internal configuration meta-data.
    /// </summary>
    private void ClearConfig()
    {
        // Other properties removed for example
        _scripts.Clear();
    }

  #region IXmlConfig

    void IXmlConfig.XmlSaveTo(int configVersion, XElement appElement)
    {
        Debug.Assert(configVersion == 2);
        Debug.Assert(appElement != null);

        // Saving of other properties removed for example

        if (_scripts.Count > 0)
        {
            var scripts = new XElement("Scripts");
            foreach (var kvp in _scripts)
            {
                var scriptElement = new XElement(kvp.Key, kvp.Value);
                scripts.Add(scriptElement);
            }
            appElement.Add(scripts);
        }
    }

    void IXmlConfig.XmlLoadFrom(int configVersion, XElement appElement)
    {
        // Implementation simplified for example

        Debug.Assert(appElement != null);
        ClearConfig();
        if (configVersion == 2)
        {
            // Loading of other configuration properites removed for example

            var scripts = appElement.Element("Scripts");
            if (scripts != null)
                foreach (var script in scripts.Elements())
                    _scripts[script.Name.ToString()] = script.Value;
        }
        else
            throw new ApplicaitonException("Unknown configuration file version " + configVersion);
    }

  #endregion
}

दुर्भाग्य से अनुक्रमित संपत्तियों को लागू नहीं किया जाता है इसलिए मैंने उन्हें संग्रहीत करने और उस तक पहुंच प्रदान करने के लिए एक वर्ग लागू किया। यह एक अवांछनीय कार्यान्वयन है क्योंकि इस डोमेन मॉडल में कॉन्फ़िगरेशन वर्ग का उद्देश्य सभी विवरणों को संक्षिप्त करना है। इस श्रेणी के ग्राहक नाम से विशिष्ट स्क्रिप्ट अंशों तक पहुंच बनाएंगे और उन पर गणना या गणना करने का कोई कारण नहीं होगा।

मैं इसे लागू कर सकता है:

public string ScriptGet(string name)
public void ScriptSet(string name, string value)

जो शायद मेरे पास होना चाहिए, लेकिन यह एक उपयोगी उदाहरण है कि क्यों इस अनुपलब्ध सुविधा के प्रतिस्थापन के रूप में अनुक्रमित कक्षाओं का उपयोग करना अक्सर उचित विकल्प नहीं होता है।

अनुक्रमित संपत्ति के रूप में इसी तरह की क्षमता को लागू करने के लिए मुझे नीचे दिए गए कोड को लिखना होगा जिसे आप देखेंगे कि यह काफी लंबा है, अधिक जटिल है और इस प्रकार पढ़ना, समझना और बनाए रखना कठिन है।

public interface IConfig
{
    // Other configuration properties removed for examp[le

    /// <summary>
    /// Script fragments
    /// </summary>
    ScriptsCollection Scripts { get; }
}

/// <summary>
/// Class to handle loading and saving the application's configuration.
/// </summary>
internal class Config : IConfig, IXmlConfig
{
    public Config()
    {
        _scripts = new ScriptsCollection();
        _scripts.ScriptChanged += ScriptChanged;
    }

  #region Application Configuraiton Settings

    // Other configuration properties removed for examp[le

    /// <summary>
    /// Script fragments
    /// </summary>
    public ScriptsCollection Scripts
    { get { return _scripts; } }
    private readonly ScriptsCollection _scripts;

    private void ScriptChanged(object sender, ScriptChangedEventArgs e)
    {
        OnAppConfigChanged();
    }

  #endregion

    /// <summary>
    /// Clears configuration settings, but does not clear internal configuration meta-data.
    /// </summary>
    private void ClearConfig()
    {
        // Other properties removed for example
        _scripts.Clear();
    }

  #region IXmlConfig

    void IXmlConfig.XmlSaveTo(int configVersion, XElement appElement)
    {
        Debug.Assert(configVersion == 2);
        Debug.Assert(appElement != null);

        // Saving of other properties removed for example

        if (_scripts.Count > 0)
        {
            var scripts = new XElement("Scripts");
            foreach (var kvp in _scripts)
            {
                var scriptElement = new XElement(kvp.Key, kvp.Value);
                scripts.Add(scriptElement);
            }
            appElement.Add(scripts);
        }
    }

    void IXmlConfig.XmlLoadFrom(int configVersion, XElement appElement)
    {
        // Implementation simplified for example

        Debug.Assert(appElement != null);
        ClearConfig();
        if (configVersion == 2)
        {
            // Loading of other configuration properites removed for example

            var scripts = appElement.Element("Scripts");
            if (scripts != null)
                foreach (var script in scripts.Elements())
                    _scripts[script.Name.ToString()] = script.Value;
        }
        else
            throw new ApplicaitonException("Unknown configuration file version " + configVersion);
    }

  #endregion
}

public class ScriptsCollection : IEnumerable<KeyValuePair<string, string>>
{
    private readonly Dictionary<string, string> Scripts = new Dictionary<string, string>();

    public string this[string name]
    {
        get
        {
            if (!string.IsNullOrWhiteSpace(name))
            {
                string script;
                if (Scripts.TryGetValue(name.Trim().ToLower(), out script))
                    return script;
            }
            return string.Empty;
        }
        set
        {
            if (!string.IsNullOrWhiteSpace(name))
                Scripts[name.Trim().ToLower()] = value;
        }
    }

    public void Clear()
    {
        Scripts.Clear();
    }

    public int Count
    {
        get { return Scripts.Count; }
    }

    public event EventHandler<ScriptChangedEventArgs> ScriptChanged;

    protected void OnScriptChanged(string name)
    {
        if (ScriptChanged != null)
        {
            var script = this[name];
            ScriptChanged.Invoke(this, new ScriptChangedEventArgs(name, script));
        }
    }

  #region IEnumerable

    public IEnumerator<KeyValuePair<string, string>> GetEnumerator()
    {
        return Scripts.GetEnumerator();
    }

    IEnumerator IEnumerable.GetEnumerator()
    {
        return GetEnumerator();
    }

  #endregion
}

public class ScriptChangedEventArgs : EventArgs
{
    public string Name { get; set; }
    public string Script { get; set; }

    public ScriptChangedEventArgs(string name, string script)
    {
        Name = name;
        Script = script;
    }
}

2

C # में अनुक्रमण का समर्थन करने वाले गुणों के आसान निर्माण में एक और वर्कअराउंड सूचीबद्ध किया गया है , जिसमें कम काम की आवश्यकता होती है।

संपादित करें : मुझे यह भी जोड़ना चाहिए कि मूल प्रश्न के उत्तर में, कि अगर मैं पुस्तकालय समर्थन के साथ वांछित वाक्यविन्यास को पूरा कर सकता हूं, तो मुझे लगता है कि इसे सीधे भाषा में जोड़ने के लिए एक बहुत मजबूत मामला होने की आवश्यकता है, ताकि भाषा ब्लोट को कम करें।


2
आपके सम्पादन के उत्तर में: मुझे नहीं लगता कि इससे भाषा प्रस्फुटित होगी; सिंटैक्स पहले से ही क्लास इंडेक्सर्स ( this[]) के लिए है, उन्हें बस इसके बजाय एक पहचानकर्ता की अनुमति देने की आवश्यकता है this। लेकिन मुझे संदेह है कि यह कभी भी भाषा में शामिल किया जाएगा, एरिक द्वारा उनके उत्तर में बताए गए कारणों के लिए
थॉमस लेवेस्क

1

वैसे मैं कहूंगा कि उन्होंने इसे जोड़ा नहीं है क्योंकि यह डिजाइन, कार्यान्वयन, परीक्षण और दस्तावेज बनाने की लागत के लायक नहीं था।

एक तरफ मजाक करते हुए, शायद इसकी वजह यह है कि वर्कअराउंड सरल हैं और सुविधा कभी भी समय बनाम लाभ में कटौती नहीं करती है। मुझे यह देखकर आश्चर्य नहीं होगा कि रेखा के नीचे एक बदलाव के रूप में दिखाई दे रहा है।

आप यह भी उल्लेख करना भूल गए कि एक आसान समाधान केवल एक नियमित विधि है:

public void SetFoo(int index, Foo toSet) {...}
public Foo GetFoo(int index) {...}

सच सच। यदि संपत्ति सिंटैक्स बिल्कुल महत्वपूर्ण है, तो आप आयन के वर्कअराउंड का उपयोग कर सकते हैं (शायद कुछ जेनरिक के साथ विभिन्न प्रकार के रिटर्न के लिए अनुमति देने के लिए)। बावजूद, मुझे लगता है कि यह अतिरिक्त भाषा सुविधाओं के बिना एक ही काम को पूरा करने के सापेक्ष आसानी से बोलता है।
रॉन वारहोलिक

1

इंडेक्सिंग कार्यक्षमता को प्रॉक्सी करने के लिए लैम्ब्डा का उपयोग करके एक सामान्य सामान्य समाधान है

केवल अनुक्रमण पढ़ने के लिए

public class RoIndexer<TIndex, TValue>
{
    private readonly Func<TIndex, TValue> _Fn;

    public RoIndexer(Func<TIndex, TValue> fn)
    {
        _Fn = fn;
    }

    public TValue this[TIndex i]
    {
        get
        {
            return _Fn(i);
        }
    }
}

परिवर्तनशील अनुक्रमण के लिए

public class RwIndexer<TIndex, TValue>
{
    private readonly Func<TIndex, TValue> _Getter;
    private readonly Action<TIndex, TValue> _Setter;

    public RwIndexer(Func<TIndex, TValue> getter, Action<TIndex, TValue> setter)
    {
        _Getter = getter;
        _Setter = setter;
    }

    public TValue this[TIndex i]
    {
        get
        {
            return _Getter(i);
        }
        set
        {
            _Setter(i, value);
        }
    }
}

और एक कारखाना है

public static class Indexer
{
    public static RwIndexer<TIndex, TValue> Create<TIndex, TValue>(Func<TIndex, TValue> getter, Action<TIndex, TValue> setter)
    {
        return new RwIndexer<TIndex, TValue>(getter, setter);
    } 
    public static RoIndexer<TIndex, TValue> Create<TIndex, TValue>(Func<TIndex, TValue> getter)
    {
        return new RoIndexer<TIndex, TValue>(getter);
    } 
}

अपने स्वयं के कोड में मैं इसका उपयोग करता हूं

public class MoineauFlankContours
{

    public MoineauFlankContour Rotor { get; private set; }

    public MoineauFlankContour Stator { get; private set; }

     public MoineauFlankContours()
    {
        _RoIndexer = Indexer.Create(( MoineauPartEnum p ) => 
            p == MoineauPartEnum.Rotor ? Rotor : Stator);
    }
    private RoIndexer<MoineauPartEnum, MoineauFlankContour> _RoIndexer;

    public RoIndexer<MoineauPartEnum, MoineauFlankContour> FlankFor
    {
        get
        {
            return _RoIndexer;
        }
    }

}

और MoineauFlankContours की एक आवृत्ति के साथ मैं कर सकता हूँ

MoineauFlankContour rotor = contours.FlankFor[MoineauPartEnum.Rotor];
MoineauFlankContour stator = contours.FlankFor[MoineauPartEnum.Stator];

चालाक, लेकिन यह बेहतर होगा कि इसे हर बार बनाने के बजाय इंडेक्स को कैश किया जाए;)
थॉमस लेवेस्क

0

बस अपने आप को यह भी पता चला है कि आप इसे प्राप्त करने के लिए स्पष्ट रूप से लागू किए गए इंटरफेस का उपयोग कर सकते हैं, जैसा कि यहां दिखाया गया है: C # में नामांकित अनुक्रमित संपत्ति? (उस उत्तर में दिखाया गया दूसरा तरीका देखें)

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.