2008-09-09 17 views
53

की सूची में किसी तत्व के मान को बदलना मेरे पास structs की एक सूची है और मैं एक तत्व बदलना चाहता हूं।structs

MyList[1].Name = "bob" 

बहरहाल, जब भी मैं कोशिश करते हैं और इस मैं निम्न त्रुटि मिलता है:

Cannot modify the return value of System.Collections.Generic.List.this[int]‘ because it is not a variable

अगर मैं एक का उपयोग

MyList.Add(new MyStruct("john"); 
MyList.Add(new MyStruct("peter"); 

अब मैं एक तत्व बदलना चाहते हैं: उदाहरण के लिए कक्षाओं की सूची, समस्या नहीं होती है।

मुझे लगता है कि उत्तर को मूल्यों के रूप में structs के साथ करना है।

तो, यदि मेरे पास structs की एक सूची है तो क्या मुझे उन्हें केवल पढ़ने के लिए के रूप में व्यवहार करना चाहिए? अगर मुझे सूची में तत्वों को बदलने की ज़रूरत है तो मुझे कक्षाओं का उपयोग करना चाहिए और structs नहीं चाहिए?

उत्तर

28
MyList[1] = new MyStruct("bob"); 

सी # में structs लगभग हमेशा अडिग होने के लिए तैयार किया जाना चाहिए (जो है, कोई रास्ता नहीं एक बार वे बनाया गया है उनके आंतरिक स्थिति को बदलने के लिए है)।

अपने मामले में, आप केवल एक ही संपत्ति या फ़ील्ड को बदलने की कोशिश न करने के लिए निर्दिष्ट सरणी अनुक्रमणिका में संपूर्ण संरचना को प्रतिस्थापित करना चाहते हैं।

+2

यह पूरा जवाब नहीं है, गिशू का जवाब बहुत अधिक पूरा है। – Motti

+0

जोलसन ने क्या कहा - यह इतना नहीं है कि structs "अपरिवर्तनीय" हैं। सही है। -1 cos यह कहना गलत है कि structs अपरिवर्तनीय हैं। – GuruC

+2

एंड्रयू के लिए उचित होने के लिए - मैं यह नहीं समझता कि वह कह रहा है कि structs "अपरिवर्तनीय" हैं, वह कह रहे हैं कि उन्हें * अगर * वे अपरिवर्तनीय हैं; और निश्चित रूप से आप उन्हें अपरिवर्तनीय बना सकते हैं यदि सभी फ़ील्ड केवल पढ़े जाते हैं। – Montdidier

38

काफी नहीं। कक्षा या संरचना के रूप में एक प्रकार को डिजाइन करना इसे संग्रह में संग्रहीत करने की आपकी ज़रूरत से प्रेरित नहीं किया जाना चाहिए :) आपको

की समस्या को देखना चाहिए, जो समस्या आप देख रहे हैं वह मान प्रकार अर्थशास्त्र के कारण है। प्रत्येक मान प्रकार चर/संदर्भ एक नया उदाहरण है। जब आप

Struct obItem = MyList[1]; 

क्या होता है यह है कि संरचना का एक नया उदाहरण बनाया गया है और सभी सदस्यों को एक-एक करके कॉपी किया गया है। तो आपके पास MyList [1] यानी 2 उदाहरणों का क्लोन है। अब यदि आप obItem को संशोधित करते हैं, तो यह मूल को प्रभावित नहीं करता है।

obItem.Name = "Gishu"; // MyList[1].Name still remains "peter" 

अब यहाँ 2 मिनट (यह नीचे घूंट कुछ समय लगता है के लिए मेरे साथ सहन .. यह मेरे लिए किया था :) तुम सच में एक संग्रह में संग्रहीत और संशोधित की तरह आप में संकेत दिया जा करने के लिए structs की जरूरत है अपने प्रश्न, आपको अपनी संरचना को एक इंटरफ़ेस का खुलासा करना होगा (हालांकि इसका परिणाम मुक्केबाजी) होगा। फिर आप इंटरफ़ेस संदर्भ के माध्यम से वास्तविक संरचना को संशोधित कर सकते हैं, जो बॉक्स किए गए ऑब्जेक्ट को संदर्भित करता है।

निम्न कोड स्निपेट दर्शाता क्या मैं सिर्फ ऊपर

public interface IMyStructModifier 
{  String Name { set;  }  } 
public struct MyStruct : IMyStructModifier ... 

List<Object> obList = new List<object>(); 
obList.Add(new MyStruct("ABC")); 
obList.Add(new MyStruct("DEF")); 

MyStruct temp = (MyStruct)obList[1]; 
temp.Name = "Gishu"; 
foreach (MyStruct s in obList) // => "ABC", "DEF" 
{  Console.WriteLine(s.Name);   } 

IMyStructModifier temp2 = obList[1] as IMyStructModifier; 
temp2.Name = "Now Gishu"; 
foreach (MyStruct s in obList) // => "ABC", "Now Gishu" 
{  Console.WriteLine(s.Name);  } 

HTH कहा। अच्छा प्रश्न।
अद्यतन: @ हैथ - आप मुझे यह जांचने के लिए दौड़ रहे थे कि मैंने कुछ आसान देखा है या नहीं। (अगर सेटर गुणों और विधियों ने किया तो यह असंगत होगा - नेट ब्रह्मांड अभी भी संतुलित है :)
सेटर विधि
obList2 [1] एक प्रतिलिपि देता है जिसका राज्य संशोधित किया जाएगा। सूची में मूल संरचना unmodified रहता है। तो सेट-थ्रू-इंटरफेस ऐसा करने का एकमात्र तरीका प्रतीत होता है।

List<MyStruct> obList2 = new List<MyStruct>(); 
obList2.Add(new MyStruct("ABC")); 
obList2.Add(new MyStruct("DEF")); 
obList2[1].SetName("WTH"); 
foreach (MyStruct s in obList2) // => "ABC", "DEF" 
{ 
    Console.WriteLine(s.Name); 
} 
+0

अभी भी कोई अच्छा नहीं है। सूची को इंटरफेस प्रकार के रूप में घोषित किया जाना चाहिए, इस मामले में इसमें सभी वस्तुओं को बॉक्स किया जाएगा। प्रत्येक मूल्य प्रकार के लिए, एक बॉक्सिंग समकक्ष * होता है जिसमें कक्षा-प्रकार अर्थशास्त्र * होता है। यदि आप कक्षा का उपयोग करना चाहते हैं, तो कक्षा का उपयोग करें, लेकिन उसके बाद अन्य ग़लत चेतावनी से अवगत रहें। – supercat

+0

@Supercat - जैसा कि मैंने ऊपर उल्लेख किया है ... यह बॉक्सिंग का कारण बन जाएगा। मैं इंटरफेस संदर्भ के माध्यम से संशोधित करने की सिफारिश नहीं कर रहा हूं - बस यह कह रहा हूं कि यदि आपके पास संग्रह structs होना चाहिए तो यह काम करेगा + उन्हें इन्हें स्थानांतरित करना चाहते हैं।यह एक हैक है .. मूल रूप से आप मूल्य-प्रकार के लिए रीफ-टाइप रैपर बना रहे हैं। – Gishu

+1

'सूची ' का उपयोग करने के बजाय या संरचना को एक सेटर इंटरफ़ेस को कार्यान्वित करने के बजाय (जैसा कि उपरोक्त वर्णित है, अर्थात् भयानक हैं), एक विकल्प 'क्लास सरलहोल्डर {सार्वजनिक टी मान परिभाषित करना है; } 'और फिर' सूची > 'का उपयोग करें। यदि 'वैल्यू' एक स्ट्रक्चर के बजाए एक फ़ील्ड है, तो एक 'ओबलिस्ट 2 [1]। वैल्यू.Name = "जॉर्ज" जैसे बयान होगा;' ठीक काम करेगा। – supercat

11

यह इतना नहीं है कि structs "अपरिवर्तनीय" हैं।

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

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

4

ऐसे सूत्रों के साथ कुछ भी गलत नहीं है जो खेतों में उजागर हुए हैं, या जो संपत्ति सेटर्स के माध्यम से उत्परिवर्तन की अनुमति देते हैं। स्ट्रक्चर जो विधियों या संपत्ति गेटर्स के जवाब में खुद को बदलते हैं, खतरनाक हैं क्योंकि सिस्टम अस्थायी संरचना के उदाहरणों पर विधियों या संपत्ति प्राप्तकर्ताओं को बुलाएगा; यदि विधियों या गेटर्स संरचना में परिवर्तन करते हैं, तो उन परिवर्तनों को त्याग दिया जाएगा।

दुर्भाग्यवश, जैसा कि आप ध्यान देते हैं, .NET में बनाए गए संग्रह वास्तव में उसमें निहित मान-प्रकार की वस्तुओं को उजागर करने में कमजोर हैं। आपकी सबसे अच्छी शर्त आमतौर पर कुछ ऐसा करने के लिए होती है:

 
    MyStruct temp = myList[1]; 
    temp.Name = "Albert"; 
    myList[1] = temp; 

कुछ हद तक परेशान, और सभी थ्रेडसेफ पर नहीं। फिर भी एक वर्ग प्रकार की एक सूची के ऊपर एक सुधार है, जहां एक ही बात कर रही है की आवश्यकता हो सकती:

 
    myList[1].Name = "Albert"; 

लेकिन यह भी आवश्यकता हो सकती है:

 
    myList[1] = myList[1].Withname("Albert"); 

या शायद

 
    myClass temp = (myClass)myList[1].Clone(); 
    temp.Name = "Albert"; 
    myList[1] = temp; 

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

+0

आप कहते हैं, "... यदि' नाम' 'MyStruct' का खुला क्षेत्र है, तो इसे अपडेट करने के लिए मैंने जो विधि दी है वह काम करेगा ..." बिल्कुल नहीं। चूंकि आपने संदर्भ 'वर्ग' मामले के लिए थ्रेड-सुरक्षा के दर्शक को उठाया है, इसलिए उसी आधार पर 'वैल्यू टाइप' कोड का न्याय करना उचित है, और सूची में इंडेक्स की स्थिति आपके ऑपरेशन के दौरान बदल सकती है जैसे कि 'myList [1 ] 'अब आपके द्वारा प्राप्त' स्ट्रक्चर 'उदाहरण से मेल नहीं खाता है। इसे ठीक करने के लिए आपको किसी प्रकार की लॉकिंग या सिंक्रनाइज़ेशन की आवश्यकता होगी जो संपूर्ण रूप से संग्रह उदाहरण से अवगत है। और 'वर्ग' संस्करण अभी भी इन समस्याओं को भी पीड़ित करता है। –

+0

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

+0

मैंने यह नहीं कहा कि यह संभव नहीं था और मुझे निश्चित रूप से इसे ठीक करने के कई तरीके पता होंगे, मैं बस आपके उदाहरण में दौड़ की स्थिति को इंगित करना चाहता था क्योंकि यह खड़ा है। शायद ऐसा इसलिए है क्योंकि इस अस्पष्ट क्षेत्र में मेरी उत्सुक सूक्ष्म विशेषज्ञता अंटार्कटिक ग्लेशियर पर उल्कापिंडों की तरह दौड़ने का कारण बनती है। "एक संग्रह जो वस्तुओं को इन-प्लेस संपादन के लिए बायफ्स के रूप में उजागर करता है:" हां, बिल्कुल; मैं इसे खुद बेहतर नहीं कह सकता था। वहां गया, ऐसा किया, महान काम करता है। ओह, और मैं लगभग भूल गया, लॉक मुक्त सहमति भी। –

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