IEnumerable का पता लगाने "राज्य मशीनों"


17

मैं सिर्फ एक दिलचस्प लेख पढ़ता हूं, जिसका नाम है सी # यील्ड रिटर्न

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

उदाहरण के लिए, आप DoubleXValue (लेख से) को कुछ इस तरह से संशोधित कर सकते हैं:

private void DoubleXValue(IEnumerable<Point> points)
{
    if(points is List<Point>)
      foreach (var point in points)
        point.X *= 2;
    else throw YouCantDoThatException();
}

प्रश्न 1) क्या ऐसा करने का एक बेहतर तरीका है?

प्रश्न 2) क्या यह कुछ है जो मुझे एपीआई बनाते समय चिंता करनी चाहिए?


1
आपको एक ठोस के बजाय एक इंटरफ़ेस का उपयोग करना चाहिए और कम डाउन भी डालना ICollection<T>चाहिए, एक बेहतर विकल्प होगा क्योंकि सभी संग्रह नहीं हैं List<T>। उदाहरण के लिए, सरणियाँ Point[]लागू होती हैं, IList<T>लेकिन नहीं हैं List<T>
ट्रेवर पिली

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

1
@JimmyHoffa IEnumerableइस अर्थ में अपरिवर्तनीय है कि आप इसे संकलित करते समय एक संग्रह (जोड़ / हटाएं) को संशोधित नहीं कर सकते हैं, हालांकि यदि सूची में मौजूद वस्तुएं परिवर्तनशील हैं, तो सूची की गणना करते समय उन्हें संशोधित करना पूरी तरह से उचित है।
ट्रेवर पिली

@TrevorPilley मैं असहमत हूं। यदि संग्रह अपरिवर्तनीय होने की उम्मीद है, तो एक उपभोक्ता से उम्मीद यह है कि सदस्यों को बाहर के अभिनेताओं द्वारा उत्परिवर्तित नहीं किया जाएगा, जिसे एक पक्ष प्रभाव कहा जाएगा और अपरिवर्तनीयता अपेक्षा का उल्लंघन करता है। पोला और कथित एलएसपी का उल्लंघन करता है।
जिमी हॉफ

कैसे उपयोग के बारे में ToList? msdn.microsoft.com/en-us/library/bb342261.aspx यह पता नहीं लगाता है कि यह उपज द्वारा उत्पन्न किया गया था, लेकिन इसके बजाय यह अप्रासंगिक बना देता है।
लुइसुबल

जवाबों:


22

आपका प्रश्न, जैसा कि मैं समझता हूँ, यह एक गलत आधार पर लगता है। मुझे देखने दो कि क्या मैं तर्क का पुनर्निर्माण कर सकता हूं:

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

समस्या यह है कि दूसरा आधार गलत है। यहां तक ​​कि अगर आप यह पता लगा सकते हैं कि क्या दिया गया IEnumerable एक पुनरावृत्ति अवरोधक परिवर्तन का परिणाम था (और हाँ, वहाँ ऐसा करने के तरीके हैं) यह मदद नहीं करेगा क्योंकि धारणा गलत है। आइए बताते हैं क्यों।

class M { public int P { get; set; } }
class C
{
  public static IEnumerable<M> S1()
  {
    for (int i = 0; i < 3; ++i) 
      yield return new M { P = i };
  }

  private static M[] ems = new M[] 
  { new M { P = 0 }, new M { P = 1 }, new M { P = 2 } };
  public static IEnumerable<M> S2()
  {
    for (int i = 0; i < 3; ++i)
      yield return ems[i];
  }

  public static IEnumerable<M> S3()
  {
    return new M[] 
    { new M { P = 0 }, new M { P = 1 }, new M { P = 2 } };
  }

  private class X : IEnumerable<M>
  {
    public IEnumerator<X> GetEnumerator()
    {
      return new XEnum();
    }
    // Omitted: non generic version
    private class XEnum : IEnumerator<X>
    {
      int i = 0;
      M current;
      public bool MoveNext()
      {
        current = new M() { P = i; }
        i += 1;
        return true;
      }
      public M Current { get { return current; } }
      // Omitted: other stuff.
    }
  }

  public static IEnumerable<M> S4()
  {
    return new X();
  }

  public static void Add100(IEnumerable<M> items)
  {
    foreach(M item in items) item.P += 100;
  }
}

ठीक है, हमारे पास चार तरीके हैं। S1 और S2 स्वतः उत्पन्न होते हैं; S3 और S4 मैन्युअल रूप से उत्पन्न अनुक्रम हैं। अब मान लें कि हमारे पास:

var items = C.Sn(); // S1, S2, S3, S4
S.Add100(items);
Console.WriteLine(items.First().P);

एस 1 और एस 4 के लिए परिणाम 0 होगा; हर बार जब आप अनुक्रम की गणना करते हैं, तो आपको एक एम बनाया गया ताज़ा संदर्भ मिलता है। S2 और S3 के लिए परिणाम 100 होगा; हर बार जब आप अनुक्रम की गणना करते हैं, तो आपको एम के लिए वही संदर्भ मिलता है जो आपको पिछली बार मिला था। अनुक्रम कोड स्वचालित रूप से उत्पन्न होता है या नहीं, इस सवाल के लिए रूढ़िवादी है कि क्या संकलित वस्तुओं में संदर्भात्मक पहचान है या नहीं। उन दो संपत्तियों - स्वचालित पीढ़ी और संदर्भीय पहचान - का वास्तव में एक-दूसरे से कोई लेना-देना नहीं है। आपके द्वारा लिंक किया गया लेख उन्हें कुछ हद तक भ्रमित करता है।

जब तक एक अनुक्रम प्रदाता को हमेशा ऑब्जेक्ट्स को संदर्भित करने के रूप में प्रलेखित नहीं किया जाता है जिनकी संदर्भात्मक पहचान होती है , तो यह मान लेना नासमझी है कि वह ऐसा करता है।


2
हम सभी जानते हैं कि आपके पास यहाँ सही उत्तर होगा, यह एक दिया गया है; लेकिन अगर आप "संदर्भात्मक पहचान" शब्द में थोड़ी और परिभाषा डाल सकते हैं तो यह अच्छा हो सकता है, मैं इसे "अपरिवर्तनीय" के रूप में पढ़ रहा हूं, लेकिन ऐसा इसलिए है क्योंकि मैं सी # में अपरिवर्तनीय को स्वीकार करता हूं क्योंकि नई वस्तु हर प्राप्त या मूल्य प्रकार को वापस कर देती है, जो मुझे लगता है कि वही बात है जिसका आप जिक्र कर रहे हैं। यद्यपि दी गई अपरिवर्तनीयता अन्य तरीकों से हो सकती है, यह C # में एकमात्र तर्कसंगत तरीका है (जो मुझे पता है, आप अच्छी तरह से जान सकते हैं कि मैं किन तरीकों से अनजान हूं)।
जिमी हॉफ

2
@JimmyHoffa: दो ऑब्जेक्ट संदर्भ x और y में "संदर्भात्मक पहचान" है यदि Object.ReferenceEquals (x, y) सही है।
एरिक लिपर्ट

हमेशा स्रोत से सीधे प्रतिक्रिया पाने के लिए अच्छा है। धन्यवाद एरिक।
ConditionRacer

10

मुझे लगता है कि मुख्य अवधारणा यह है कि आपको किसी भी IEnumerable<T>संग्रह को अपरिवर्तनीय मानना ​​चाहिए : आपको इसमें से वस्तुओं को संशोधित नहीं करना चाहिए, आपको नई वस्तुओं के साथ एक नया संग्रह बनाना चाहिए:

private IEnumerable<Point> DoubleXValue(IEnumerable<Point> points)
{
    foreach (var point in points)
        yield return new Point(point.X * 2, point.Y);
}

(या LINQ का उपयोग करते हुए अधिक समान रूप से लिखें Select()।)

यदि आप वास्तव में संग्रह में वस्तुओं को संशोधित करना चाहते हैं, तो उपयोग न करें IEnumerable<T>, उपयोग करें IList<T>(या संभवतः IReadOnlyList<T>यदि आप संग्रह को स्वयं संशोधित नहीं करना चाहते हैं, तो इसमें केवल आइटम हैं, और आप .Net 4.5 पर हैं):

private void DoubleXValue(IList<Point> points)
{
    foreach (var point in points)
        point.X *= 2;
}

इस तरह, अगर कोई आपके तरीके का उपयोग करने का प्रयास करता है, तो IEnumerable<T>यह संकलन समय पर विफल हो जाएगा।


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

1
कुछ हद तक संबंधित - आपको प्रत्येक IEnumerable का भी इलाज करना चाहिए जैसे कि यह आस्थगित निष्पादन के माध्यम से किया जाता है। जब आप अपने IEnumerable संदर्भ को पुनः प्राप्त करते हैं, तो यह सच नहीं हो सकता है जब आप वास्तव में इसके माध्यम से गणना करते हैं। उदाहरण के लिए, एक लॉक के अंदर एक LINQ स्टेटमेंट वापस करने से कुछ दिलचस्प अप्रत्याशित परिणाम हो सकते हैं।
डैन ल्योंस

@ जिमीहॉफ: मैं सहमत हूं। मैं एक कदम आगे जाता हूं और एक IEnumerableसे अधिक बार पुनरावृति से बचने की कोशिश करता हूं । एक बार से अधिक ऐसा करने की आवश्यकता को कॉल करके ToList()और सूची से अधिक पुनरावृत्ति करके देखा जा सकता है , हालांकि ओपी द्वारा दिखाए गए विशिष्ट मामले में मैं डबलएक्सवैल्यू होने के svick के समाधान को प्राथमिकता देता हूं जो एक IEnumerable भी लौटाता है। बेशक, वास्तविक कोड में मैं शायद LINQइस प्रकार का उपयोग करने के लिए होगा IEnumerable। हालांकि, ओप yieldके प्रश्न के संदर्भ में svick की पसंद समझ में आती है।
ब्रायन

@Brian हाँ, मैं अपनी बात बनाने के लिए कोड को बहुत अधिक नहीं बदलना चाहता था। असली कोड में, मैं इस्तेमाल किया होता Select()। और कई पुनरावृत्तियों के बारे में IEnumerable: ReSharper इसके साथ मददगार है, क्योंकि यह आपको चेतावनी देता है कि जब आप ऐसा करेंगे।
२१:३३ पर २१:१३ पर svick

5

मुझे व्यक्तिगत रूप से लगता है कि उस लेख में उजागर समस्या IEnumerableइन दिनों कई सी # डेवलपर्स द्वारा इंटरफ़ेस के अति प्रयोग से उपजी है । ऐसा लगता है कि जब आप वस्तुओं के संग्रह की आवश्यकता कर रहे हैं तो यह डिफ़ॉल्ट प्रकार बन गया है - लेकिन बहुत सी जानकारी है जो आपको IEnumerableआसानी से नहीं बताती है ... महत्वपूर्ण रूप से: चाहे वह पुनरीक्षित हो या नहीं।

यदि आप पाते हैं कि आपको यह पता लगाने की आवश्यकता है कि क्या IEnumerableवास्तव में एक IEnumerableया कुछ और है, तो अमूर्त रिसाव हो गया है और आप शायद ऐसी स्थिति में हैं जहां आपका पैरामीटर प्रकार थोड़ा बहुत ढीला है। यदि आपको अतिरिक्त जानकारी की आवश्यकता होती है जो IEnumerableअकेले प्रदान नहीं करती है, तो उस आवश्यकता को अन्य इंटरफेस जैसे कि ICollectionया में से किसी एक को चुनकर स्पष्ट करें IList

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

* मैं शब्द का उपयोग करता हूं reify, अन्य लोग उपयोग करते हैं stabilizeया materialize। मुझे नहीं लगता कि समुदाय द्वारा अभी तक एक आम सम्मेलन नहीं अपनाया गया है।


2
के साथ एक समस्या ICollectionऔर IListहै कि वे परिवर्तनशील हैं (या कम से कम नज़र तरीका है कि) है। IReadOnlyCollectionऔर IReadOnlyList.net 4.5 से उन समस्याओं में से कुछ को हल करते हैं।
svick

कृपया नीचे बताएं?
मैटवेवी

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

केवल ienumerable, आस्थगित निष्पादन की अनुमति देता है, इसलिए इसे एक पैरामीटर के रूप में अस्वीकार करना C # की एक पूरी सुविधा को हटा देता है
जिमी हॉफ

2
मैंने इसे वोट दिया क्योंकि मुझे लगता है कि यह गलत है। मुझे लगता है कि यह एसई की भावना में है, यह सुनिश्चित करने के लिए कि लोग गलत सूचनाओं में स्टॉक नहीं डालते हैं, यह हम सभी के लिए है कि उन उत्तरों को वोट दें। शायद मैं गलत हूं, पूरी तरह से संभव है कि मैं गलत हूं और आप सही हैं। यह मतदान करने का निर्णय लेने के लिए व्यापक समुदाय तक है। क्षमा करें, आप इसे व्यक्तिगत रूप से ले रहे हैं, अगर यह कोई सांत्वना है, तो मेरे पास लिखे गए और नकारात्मक रूप से हटाए गए कई नकारात्मक वोट हैं क्योंकि मैं स्वीकार करता हूं कि मुझे लगता है कि यह गलत है इसलिए मैं सही नहीं हूं।
जिमी हॉफ
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.