2011-04-13 9 views
52

मुझे पता है कि मूल्य प्रकार अपरिवर्तनीय होना चाहिए, लेकिन यह सिर्फ एक सुझाव है, नियम नहीं, है ना?सी # में, मैं फ़ोरैच लूप में मान प्रकार के उदाहरण के सदस्य को क्यों संशोधित नहीं कर सकता?

struct MyStruct 
{ 
    public string Name { get; set; } 
} 

public class Program 
{ 
    static void Main(string[] args) 
    { 
     MyStruct[] array = new MyStruct[] { new MyStruct { Name = "1" }, new MyStruct { Name = "2" } }; 
     foreach (var item in array) 
     { 
      item.Name = "3"; 
     } 
     //for (int i = 0; i < array.Length; i++) 
     //{ 
     // array[i].Name = "3"; 
     //} 

     Console.ReadLine(); 
    } 
} 

कोड में foreach पाश जबकि पाश के लिए टिप्पणी की ठीक काम करता है संकलन नहीं करता है: तो मैं कुछ इस तरह क्यों नहीं कर सकते। त्रुटि संदेश:

'आइटम' के सदस्यों संशोधित नहीं कर सकते, क्योंकि यह एक 'foreach यात्रा चर'

कि क्यों है?

+3

+1: अच्छा प्रश्न है। मुझे लंबे समय से पता चला है कि 'foreach' लूप वैरिएबल को संशोधित नहीं किया जा सका, लेकिन मैंने कभी नहीं सीखा ** ** क्यों! – jp2code

उत्तर

54

foreach एक प्रगणक का उपयोग करता है, और प्रगणक अंतर्निहित संग्रह नहीं बदल सकते, लेकिन , हालांकि, किसी भी वस्तुओं संग्रह में एक वस्तु के द्वारा संदर्भित बदल सकते हैं। यह वह जगह है जहां मूल्य और संदर्भ-प्रकार अर्थशास्त्र खेल में आते हैं।

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

दूसरी ओर, मूल्य प्रकार संग्रह में अपनी संपूर्ण संरचना को संग्रहीत करते हैं। आप संग्रह को बदलने और गणनाकर्ता को अमान्य किए बिना अपने सदस्यों को स्पर्श नहीं कर सकते हैं।

इसके अलावा, गणनाकर्ता संग्रह में मूल्य की प्रतिलिपि देता है। एक प्रकार के प्रकार में, इसका मतलब कुछ भी नहीं है। किसी संदर्भ की एक प्रति एक ही संदर्भ होगी, और आप संदर्भित ऑब्जेक्ट को किसी भी तरह से बदल सकते हैं जिसमें आप स्कोप से फैले हुए परिवर्तनों के साथ चाहते हैं। दूसरी तरफ, मूल्य-प्रकार पर, इसका मतलब है कि आपको ऑब्जेक्ट की प्रतिलिपि मिलती है, और इस प्रकार कहा गया प्रतिलिपि में कोई भी बदलाव कभी प्रचार नहीं करेगा।

+0

उत्कृष्ट उत्तर। और "मूल्य प्रकार संग्रह में उनकी पूरी संरचना को स्टोर करने" के बारे में अधिक सबूत कहां मिल सकता है? – CuiPengFei

+0

@CuiPengFei: वैल्यू प्रकार एक वैरिएबल में अपना पूरा मान स्टोर करते हैं। Arrays केवल चर के एक ब्लॉक हैं। –

+0

बिल्कुल। एक वैल्यू टाइप अपनी पूरी संरचना को जो भी कंटेनर में रखा गया है, चाहे वह एक संग्रह हो, स्थानीय वैर इत्यादि। संयोग से, यही कारण है कि structs को अपरिवर्तनीय बनाना बेहतर है: आप "की समान प्रतियों के साथ समाप्त होते हैं "ऑब्जेक्ट और यह ट्रैक रखना मुश्किल है कि किसने बदलाव किया और क्या यह प्रचार करेगा और कहां होगा। पता है, तारों के साथ होने वाली सामान्य समस्याएं। – Kyte

2

नोट:एडम की टिप्पणी के अनुसार, यह वास्तव में समस्या का सही उत्तर/कारण नहीं है। हालांकि यह अभी भी दिमाग में कुछ लायक है।

MSDN से। आप गणनाकर्ता का उपयोग करते समय मानों को संशोधित नहीं कर सकते हैं, जो अनिवार्य रूप से foreach कर रहा है।

ूगणकों संग्रह में डेटा पढ़ने के लिए इस्तेमाल किया जा सकता है, लेकिन वे अंतर्निहित संग्रह संशोधित करने के लिए नहीं किया जा सकता।

एक गणक तक वैध रहता है जब संग्रह अपरिवर्तित रहता है। यदि संग्रह में जैसे परिवर्तन तत्वों को जोड़ने, संशोधित करने या हटाने के लिए किए गए हैं, तो गणनाकर्ता अप्राप्य रूप से अमान्य है और इसके व्यवहार को अपरिभाषित किया गया है।

+1

हालांकि यह ऐसा दिखाई दे सकता है, यह सवाल में कोई मुद्दा नहीं है। आप 'foreach' (जो एमएसडीएन आलेख के बारे में बात नहीं कर रहा है) के माध्यम से प्राप्त एक उदाहरण के गुण या फ़ील्ड मानों को निश्चित रूप से संशोधित कर सकता है, क्योंकि यह संग्रह के * सदस्य * को संशोधित कर रहा है, न कि संग्रह स्वयं। यहां मुद्दा मूल्य-प्रकार बनाम संदर्भ-प्रकार अर्थशास्त्र है। –

+0

वास्तव में, मैंने अभी आपका जवाब पढ़ लिया है और मेरे तरीकों से गलती को महसूस किया है। मैं इसे छोड़ दूंगा क्योंकि यह अभी भी उपयोगी है। – Ian

+0

धन्यवाद, लेकिन मुझे नहीं लगता कि यह कारण है। क्योंकि मैं संग्रह के तत्व के सदस्य में से किसी एक को बदलने की कोशिश कर रहा हूं, संग्रह स्वयं ही नहीं। और यदि मैं एक स्ट्रक्चर से एक वर्ग में MyStruct को बदलता हूं, तो फ़ोरैच लूप बस काम करेगा। – CuiPengFei

14

यह इस अर्थ में एक सुझाव है कि इसका उल्लंघन करने से आपको कुछ भी रोक नहीं रहा है, लेकिन इसे वास्तव में "यह एक सुझाव" से अधिक वजन देना चाहिए। उदाहरण के लिए, आप यहां देख रहे कारणों के लिए।

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

यदि आप ऐसा करना चाहते हैं, तो आपको इंडेक्स द्वारा सरणी पर लूप करना होगा और अस्थायी चर का उपयोग नहीं करना होगा।

+0

डाउनवॉटर टिप्पणी करने की देखभाल करते हैं? –

+0

मैंने डाउनवोट नहीं किया, लेकिन मेरा मानना ​​है [कारण आप उल्लेख करते हैं कि क्यों प्रचार पढ़ा जाता है पूरी तरह से सही नहीं है] (http://stackoverflow.com/questions/4004755/why-is-foreach-loop-read-only- इन-सी/4004778 # 4004778)। –

+0

@Steven: मैंने जो कारण बताया है वह सही है, अगर लिंक किए गए उत्तर में पूरी तरह वर्णित नहीं है। –

1

MyStruct को एक वर्ग (संरचना के बजाय) बनाएं और आप ऐसा करने में सक्षम होंगे।

+0

यह सही VT (मान प्रकार) बनाम आरटी (रेफ प्रकार) पर आधारित है लेकिन वास्तव में सवाल का जवाब नहीं देता है। – JonH

+0

आप सही हैं। मैं यहाँ बिंदु चूक गया। –

+0

धन्यवाद, मुझे पता है कि यह काम करेगा। लेकिन मैं सिर्फ कारण जानने का प्रयास कर रहा हूं। – CuiPengFei

1

मूल्य प्रकार called by value हैं। संक्षेप में, जब आप चर का मूल्यांकन करते हैं, तो इसकी एक प्रति बनाई जाती है। यहां तक ​​कि अगर इसकी अनुमति थी, तो आप MyStruct के मूल उदाहरण के बजाय एक प्रतिलिपि संपादित करेंगे।

foreach is readonly in C#। संदर्भ प्रकार (वर्ग) के लिए यह बहुत बदल नहीं है, के रूप में केवल संदर्भ केवल पढ़ने के लिए है, तो आप अभी भी निम्न कार्य करने की अनुमति दी जाती है:

MyClass[] array = new MyClass[] { 
     new MyClass { Name = "1" }, 
     new MyClass { Name = "2" } 
    }; 
    foreach (var item in array) 
    { 
     item.Name = "3"; 
    } 

मूल्य प्रकार तथापि (struct) के लिए, संपूर्ण वस्तु केवल पढ़ने के लिए है जिसके परिणामस्वरूप आप अनुभव कर रहे हैं। आप ऑब्जेक्ट को फ़ोरैच में समायोजित नहीं कर सकते हैं।

+0

धन्यवाद, लेकिन मुझे नहीं लगता कि आपकी व्याख्या 100% प्रतिशत सही है। क्योंकि अगर मैं MyStruct में ChangeName विधि में जोड़ता हूं और उसे फ़ोरैच लूप में कॉल करता हूं, तो यह ठीक काम करेगा। – CuiPengFei

+0

@CuiPengFei: यह संकलित करता है, लेकिन यदि आप मूल उदाहरणों को बाद में सत्यापित करते हैं, तो आप देखेंगे कि वे नहीं बदले हैं। (मूल्य प्रकार के 'मूल्य द्वारा कॉल' व्यवहार के कारण।) –

+0

ओह, हाँ, सावधान टिप्पणी के लिए खेद है। – CuiPengFei

8

संरचनाएं मूल्य प्रकार हैं।
कक्षाएं संदर्भ प्रकार हैं।

ForEach निर्माण IEnumerator IEnumerable डेटा प्रकार तत्वों की उपयोग करता है। जब ऐसा होता है, तो वेरिएबल केवल अर्थ में पढ़े जाते हैं, कि आप उन्हें संशोधित नहीं कर सकते हैं, और जैसा कि आपके पास मूल्य प्रकार है, तो आप किसी भी मान को शामिल नहीं कर सकते हैं, क्योंकि यह समान स्मृति साझा करता है।

सी # लैंग कल्पना खंड 8.8.4:

यात्रा चर एक गुंजाइश है कि एम्बेडेड बयान

इस प्रयोग के वर्ग ठीक करने के लिए पर फैली साथ एक केवल पढ़ने के लिए स्थानीय चर से मेल खाती है insted संरचना का;

class MyStruct { 
    public string Name { get; set; } 
} 

संपादित करें: @CuiPengFei

आप var निहित प्रकार का उपयोग करते हैं, यह कठिन है संकलक तुम्हारी मदद करने के लिए। यदि आप MyStruct का उपयोग करेंगे तो यह आपको संरचना के मामले में बताएगा, कि यह केवल पढ़ने योग्य है। कक्षाओं के मामले में आइटम का संदर्भ केवल पढ़ने के लिए है, इसलिए आप लूप के अंदर item = null; नहीं लिख सकते हैं, लेकिन आप इसके गुणों को बदल सकते हैं, जो उत्परिवर्तनीय हैं।

तुम भी (यदि आप struct का उपयोग करना चाहते) का उपयोग कर सकते हैं:

 MyStruct[] array = new MyStruct[] { new MyStruct { Name = "1" }, new MyStruct { Name = "2" } }; 
     for (int index=0; index < array.Length; index++) 
     { 
      var item = array[index]; 
      item.Name = "3"; 
     } 
+0

धन्यवाद, लेकिन आप इस तथ्य को कैसे समझाते हैं कि यदि मैं MyStruct में चेंजनाम विधि जोड़ता हूं और उसे फ़ोरैच लूप में कॉल करता हूं, तो यह ठीक काम करेगा? – CuiPengFei

+0

मेरी आखिरी टिप्पणी गलत है, क्षमा करें। – CuiPengFei

संबंधित मुद्दे