2011-09-03 15 views
6

हाय, मेरे पास निम्न कोड है जो एक अजीब व्यवहार पैदा करता है। लिनक से ऑब्जेक्ट्स द्वारा उत्पादित आईन्यूमेरेबल में निहित ऑब्जेक्ट्स के उदाहरण की एक संपत्ति, बाद के पूर्वानुमान विवरणों में अपडेट नहीं होती है। Foreach कथन INumerable enuemerate करना चाहिए। इसके बजाय समाधान पहले इसे गिनना है।linq C# में अजीब व्यवहार निष्पादन में देरी के तहत

हालांकि मुझे समाधान मिला है, लेकिन मैंने इसे कहीं भी किताबों या लेखों में दस्तावेज नहीं देखा है, इसी तरह के उदाहरणों से निपटना। शायद लिनक के जटिल ज्ञान वाले किसी को यह समझा सकता है।

मुझे त्रुटि के सटीक कारण को इंगित करने में एक दिन लगा, और बड़े एप्लिकेशन में डीबग करना आसान नहीं है। इसके बाद मैंने इसे प्रस्तुत किए गए एक बहुत ही सरल वातावरण में पुन: उत्पन्न किया।

public class MyClass 
{ 
    public int val ; 
} 

public class MyClassExtrax 
{ 
    public MyClass v1 { get; set; } 
    public int prop1 { get; set; } 
} 

void Main() 
{ 
List <MyClass> list1 = new List<MyClass>(); 
MyClass obj1 = new MyClass(); obj1.val = 10; 
list1.Add(obj1); 
MyClass obj2 = new MyClass(); 
obj2.val = 10; 
list1.Add(obj2); 

IEnumerable<MyClassExtrax> query1 = 
    from v in list1 
    where v.val >= 0 
    select new MyClassExtrax{ v1=v , prop1=0 } ; 

//query1=query1.ToList(); solves the problem..but why is this needed..? 
foreach (MyClassExtrax fj in query1) 
    { 
    fj.v1.val = 40; 
    fj.prop1 = 40; //property does not get updated.. 
    } 


foreach (MyClass obj in list1) 
    { 
    Console.WriteLine("in list 1 value is {0} : ", obj.val); 
    } 


foreach (MyClassExtrax obj in query1) 
    { 
    Console.WriteLine("in MyClassExtra list v1.val is {0}, prop1 is {1} ", obj.v1.val, obj.prop1); 
    } 

} 

उत्पादन: सूची में 1 मूल्य 40 है:

MyClassExtra सूची v1.val में

40 है, prop1 0

MyClassExtra में

है:

सूची में 1 मूल्य 40 है सूची v1.val 40 है, prop1 0

जैसा कि आप देख सकते हैं कि prop1 को 40 तक अपडेट नहीं किया गया है। !!

उत्तर

7

यह काफी आसान है, यह well-documented है। यह LINQ के deferred execution feature की वजह से है। यह कोड

IEnumerable<MyClassExtrax> query1 = 
from v in list1 
where v.val >= 0 
select new MyClassExtrax{ v1=v , prop1=0 } ; 

वास्तव में वस्तुओं को नहीं बनाता है। यह सिर्फ एक वस्तु बनाता है जो वास्तव में वस्तु को बनाता है जब वस्तु उत्पन्न करता है। यही है, आप query1 के नियम के रूप में सोच सकते हैं जो ऑब्जेक्ट्स को थूकना है, जब उन्हें से अनुरोध किया जाता है। तो अगर आप ऐसा करते हैं जब:

foreach (MyClassExtrax fj in query1) 
{ 
    fj.v1.val = 40; 
    fj.prop1 = 40; //property does not get updated.. 
} 

और फिर इस:

foreach (MyClassExtrax obj in query1) 
{ 
    Console.WriteLine("in MyClassExtra list v1.val is {0}, prop1 is {1} ", obj.v1.val, obj.prop1); 
} 

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

हालांकि, जब आप ToList पर कॉल करते हैं, और फिर परिणामस्वरूप सूची चलते हैं, तो अब आपने ऑब्जेक्ट्स का एक अनुक्रम बनाया है, और ऑब्जेक्ट्स का अनुक्रम स्पष्ट रूप से दो पुनरावृत्तियों में समान है।

+0

धन्यवाद आप सही हैं। संपत्ति अद्यतन हो जाती है। उदाहरण के लिए Console.WriteLine डालने पर ("MyClassExtra सूची v1.val में {0} है, prop1 {1}", fj.v1.val, fj.prop1) है; पहले पाश में। लूप के लिए दूसरे में यह MyClassExtrax के उदाहरणों के लिए एक नया संदर्भ बना रहा है। – gregor

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