क्या करता है विज़ुअल स्टूडियो डीबगर ने एक ToString ओवरराइड का मूल्यांकन करना बंद कर दिया है?


221

पर्यावरण: विज़ुअल स्टूडियो 2015 आरटीएम। (मैंने पुराने संस्करणों की कोशिश नहीं की है।)

हाल ही में, मैं अपने कुछ Noda Time कोड डीबग कर रहा हूं , और मैंने देखा है कि जब मुझे स्थानीय प्रकार का NodaTime.Instant( structNoda Time में केंद्रीय प्रकारों में से एक ) मिला है, तो "लोकल" और "वॉच" विंडो इसके ToString()ओवरराइड को कॉल करने के लिए प्रकट न हों । अगर मैं ToString()वॉच विंडो में स्पष्ट रूप से कॉल करता हूं , तो मुझे उचित प्रतिनिधित्व दिखाई देता है, लेकिन अन्यथा मैं सिर्फ देखता हूं:

variableName       {NodaTime.Instant}

जो बहुत उपयोगी नहीं है।

अगर मैं एक निरंतर स्ट्रिंग वापस जाने के लिए ओवरराइड बदलने के लिए, स्ट्रिंग है , डिबगर में दिखाया गया है तो यह स्पष्ट रूप से लेने के लिए है कि यह वहाँ में सक्षम है - यह सिर्फ अपनी "सामान्य" राज्य में इसका इस्तेमाल नहीं करना चाहता है।

मैंने इसे थोड़ा डेमो ऐप में स्थानीय रूप से पुन: पेश करने का फैसला किया, और यहाँ मैं इसके साथ आया हूं। (ध्यान दें कि इस पोस्ट के शुरुआती संस्करण में, DemoStructएक वर्ग DemoClassथा और बिल्कुल भी मौजूद नहीं था - मेरी गलती है, लेकिन यह कुछ टिप्पणियों की व्याख्या करता है जो अब अजीब लगते हैं ...)

using System;
using System.Diagnostics;
using System.Threading;

public struct DemoStruct
{
    public string Name { get; }

    public DemoStruct(string name)
    {
        Name = name;
    }

    public override string ToString()
    {
        Thread.Sleep(1000); // Vary this to see different results
        return $"Struct: {Name}";
    }
}

public class DemoClass
{
    public string Name { get; }

    public DemoClass(string name)
    {
        Name = name;
    }

    public override string ToString()
    {
        Thread.Sleep(1000); // Vary this to see different results
        return $"Class: {Name}";
    }
}

public class Program
{
    static void Main()
    {
        var demoClass = new DemoClass("Foo");
        var demoStruct = new DemoStruct("Bar");
        Debugger.Break();
    }
}

डिबगर में, मैं अब देखता हूं:

demoClass    {DemoClass}
demoStruct   {Struct: Bar}

हालाँकि, अगर मैं Thread.Sleepकॉल को 1 सेकंड से घटाकर 900ms कर देता हूं, तब भी एक छोटा विराम है, लेकिन फिर मैं Class: Fooमूल्य के रूप में देखता हूं । इससे कोई फर्क नहीं पड़ता कि Thread.Sleepकॉल कितने समय के लिए है DemoStruct.ToString(), यह हमेशा ठीक से प्रदर्शित होता है - और डिबगर सोने से पहले मूल्य को प्रदर्शित करता है। (ऐसा लगता Thread.Sleepहै जैसे विकलांग है।)

अब Instant.ToString()नोदा टाइम में उचित मात्रा में काम किया जाता है, लेकिन यह निश्चित रूप से एक दूसरे को पूरा नहीं करता है - इसलिए संभवतः अधिक स्थितियां हैं जो डिबगर को ToString()कॉल का मूल्यांकन करने का कारण बनाती हैं। और निश्चित रूप से यह वैसे भी एक संरचना है।

मैंने यह देखने की कोशिश की है कि क्या यह एक स्टैक सीमा है, लेकिन ऐसा प्रतीत नहीं होता है।

तो, मैं कैसे काम कर सकता हूं जो वीएस को पूरी तरह से मूल्यांकन करने से रोक रहा है Instant.ToString()? के रूप में नीचे वर्णित है, DebuggerDisplayAttributeमदद के लिए प्रकट होता है, लेकिन जाने बिना क्यों , मैं कभी में जब मैं इसकी आवश्यकता पूरी तरह आश्वस्त होने के लिए जा रहा हूँ और नहीं जब मैं करता हूँ।

अपडेट करें

अगर मैं उपयोग करता हूं DebuggerDisplayAttribute, तो चीजें बदल जाती हैं:

// For the sample code in the question...
[DebuggerDisplay("{ToString()}")]
public class DemoClass

मुझे देता है:

demoClass      Evaluation timed out

जब मैं इसे Noda Time में लागू करता हूं:

[DebuggerDisplay("{ToString()}")]
public struct Instant

एक साधारण परीक्षण ऐप मुझे सही परिणाम दिखाता है:

instant    "1970-01-01T00:00:00Z"

तो संभवतः Noda Time में समस्या कुछ ऐसी स्थिति है जो DebuggerDisplayAttribute इसके माध्यम से बल देती है - भले ही यह समयबाह्य के माध्यम से बल नहीं देती है। (यह मेरी अपेक्षा के अनुरूप होगा Instant.ToStringजो टाइमआउट से बचने के लिए आसानी से पर्याप्त है।)

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

अजीब तरह से

डिबगर जो भी भ्रमित कर रहा है वह केवल इसे कभी-कभी भ्रमित करता है। आइए एक वर्ग बनाएं जो इसे धारण करता है Instantऔर इसे अपनी ToString()विधि के लिए उपयोग करता है :

using NodaTime;
using System.Diagnostics;

public class InstantWrapper
{
    private readonly Instant instant;

    public InstantWrapper(Instant instant)
    {
        this.instant = instant;
    }

    public override string ToString() => instant.ToString();
}

public class Program
{
    static void Main()
    {
        var instant = NodaConstants.UnixEpoch;
        var wrapper = new InstantWrapper(instant);

        Debugger.Break();
    }
}

अब मैं देख रहा हूं:

instant    {NodaTime.Instant}
wrapper    {1970-01-01T00:00:00Z}

हालांकि, टिप्पणियों में एरेन के सुझाव पर, अगर मैं InstantWrapperएक संरचना बनने के लिए बदलूं, तो मुझे यह मिलेगा:

instant    {NodaTime.Instant}
wrapper    {InstantWrapper}

तो यह मूल्यांकन कर सकता है Instant.ToString()- जब तक कि इसे किसी अन्य ToStringविधि द्वारा लागू किया जाता है ... जो कि एक वर्ग के भीतर है। वर्ग / संरचना वाला भाग प्रदर्शित होने वाले चर के आधार पर महत्वपूर्ण लगता है, न कि परिणाम प्राप्त करने के लिए किस कोड को निष्पादित करने की आवश्यकता होती है।

इसका एक और उदाहरण के रूप में, यदि हम उपयोग करते हैं:

object boxed = NodaConstants.UnixEpoch;

... तो यह ठीक काम करता है, सही मूल्य प्रदर्शित करता है। मुझे उलझन में रंग।


7
@ 2013 में वीएस के समान व्यवहार (मुझे सी # 6 सामान को हटाना पड़ा), एक अतिरिक्त संदेश के साथ: नाम फ़ंक्शन मूल्यांकन अक्षम क्योंकि पिछले कार्य मूल्यांकन का समय समाप्त हो गया। आपको पुन: प्रयोज्य फ़ंक्शन मूल्यांकन के लिए निष्पादन जारी रखना चाहिए। स्ट्रिंग
वीसी 74

1
c # 6.0 @ 3-14159265358979323846264 पर आपका स्वागत है
नील

1
शायद DebuggerDisplayAttributeयह एक छोटे से कठिन प्रयास करने का कारण होगा।
जूल

1
5 सी बिंदु neelbhatt40.wordpress.com/2015/07/13/… पर देखें # 3-14159265358979323846264 नए c # 6.0 के लिए
नील

5
@DiomidisSpinellis: अच्छी तरह से मैंने यहाँ पर यह पूछा है कि क) कोई ऐसा व्यक्ति जिसने या तो पहले एक ही चीज़ देखी हो या जो वीएस के अंदर का जवाब जानता हो; ख) भविष्य में एक ही मुद्दे पर चलने वाले किसी भी व्यक्ति को जवाब जल्दी मिल सकता है।
जॉन स्कीट

जवाबों:


193

अपडेट करें:

यह बग विजुअल स्टूडियो 2015 अपडेट 2 में तय किया गया है। मुझे बताएं कि क्या आप अभी भी 2 या बाद के अपडेट का उपयोग करते हुए संरचनात्मक मूल्यों पर ToString के मूल्यांकन में समस्याओं में चल रहे हैं।

मूल उत्तर:

आप Visual Studio 2015 के साथ एक ज्ञात बग / डिज़ाइन सीमा में चल रहे हैं और संरचनात्मक प्रकारों पर ToString कॉल कर रहे हैं। इससे निपटने के दौरान भी देखा जा सकता है System.DateTimeSpanSystem.DateTimeSpan.ToString()विज़ुअल स्टूडियो 2013 के साथ मूल्यांकन विंडो में काम करता है, लेकिन हमेशा 2015 में काम नहीं करता है।

यदि आप निम्न स्तर के विवरण में रुचि रखते हैं, तो यहां क्या हो रहा है:

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

लैम्ब्डा एक्सप्रेशन का समर्थन करने के लिए, हमें Visual Studio 2015 में CLR एक्सप्रेशन इवैल्यूएटर को पूरी तरह से फिर से लिखना था। उच्च स्तर पर, कार्यान्वयन है:

  1. रोज़लिन विभिन्न निरीक्षण खिड़कियों में प्रदर्शित होने वाले मूल्यों को प्राप्त करने के लिए भाव / स्थानीय चर के लिए एमएसआईएल कोड उत्पन्न करता है।
  2. डिबगर परिणाम प्राप्त करने के लिए IL की व्याख्या करता है।
  3. यदि कोई "कॉल" निर्देश हैं, तो डिबगर एक फ़ंक्शन मूल्यांकन को निष्पादित करता है जैसा कि ऊपर वर्णित है।
  4. डिबगर / रोसलिन इस परिणाम को लेता है और इसे उपयोगकर्ता की तरह दिखाए गए ट्री-व्यू में प्रारूपित करता है।

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

उस सब को ध्यान में रखते हुए, यहाँ वह विभिन्न व्यवहार हैं जो आप देख रहे हैं:

  1. डीबगर का मूल्यांकन नहीं कर रहा है NodaTime.Instant.ToString-> यह इसलिए है क्योंकि यह संरचना प्रकार है और ऊपर वर्णित डीबगर द्वारा ToString के कार्यान्वयन का अनुकरण नहीं किया जा सकता।
  2. Thread.Sleepजब ToStringकिसी संरचना पर कॉल किया जाता है तो शून्य समय लगता है -> ऐसा इसलिए है क्योंकि एमुलेटर निष्पादित हो रहा है ToString। Thread.Sleep एक देशी विधि है, लेकिन एमुलेटर को इसके बारे में पता है और बस कॉल को अनदेखा करता है। हम ऐसा करने की कोशिश करते हैं और उपयोगकर्ता को दिखाने के लिए एक मूल्य प्राप्त करते हैं। इस मामले में देरी मददगार नहीं होगी।
  3. DisplayAttibute("ToString()")काम करता है। -> यह भ्रामक है। केवल के अंतर्निहित बुला के बीच का अंतर ToStringऔर DebuggerDisplayहै कि निहित के किसी भी समय बहिष्कार है ToString मूल्यांकन सभी अंतर्निहित को निष्क्रिय कर देगा ToStringकि प्रकार अगले डिबग सत्र तक के लिए मूल्यांकन। आप उस व्यवहार को देख रहे होंगे।

डिज़ाइन समस्या / बग के संदर्भ में, यह कुछ ऐसा है जिसे हम भविष्य के विज़ुअल स्टूडियो की रिलीज़ में संबोधित करने की योजना बना रहे हैं।

उम्मीद है कि चीजों को साफ करता है। यदि आपके और प्रश्न हैं तो मुझे बताएं। :-)


1
किसी भी विचार कैसे Instant.ToString काम करता है अगर कार्यान्वयन सिर्फ "एक स्ट्रिंग शाब्दिक वापसी" है? ऐसा लगता है कि कुछ जटिलताएँ अभी भी बेहिसाब हैं :) मैं जाँच करूँगा कि मैं वास्तव में उस व्यवहार को पुन: पेश कर सकता हूँ ...
जॉन स्कीट

1
@, मुझे यकीन नहीं है कि आप क्या पूछ रहे हैं। डिबगर वास्तविक फ़ंक्शन मूल्यांकन करते समय कार्यान्वयन का अज्ञेय है और यह हमेशा पहले प्रयास करता है। डिबगर केवल कार्यान्वयन के बारे में परवाह करता है जब उसे कॉल का अनुकरण करने की आवश्यकता होती है - एक स्ट्रिंग शाब्दिक लौटना अनुकरण करने के लिए सबसे सरल मामला है।
पैट्रिक नेल्सन - MSFT

8
आदर्श रूप से हम चाहते हैं कि CLR सब कुछ निष्पादित करे। यह सबसे सटीक और विश्वसनीय परिणाम प्रदान करता है। इसलिए हम ToString कॉल के लिए वास्तविक फ़ंक्शन मूल्यांकन करते हैं। जब यह संभव नहीं होता है, तो हम कॉल का अनुकरण करने के लिए वापस आते हैं। इसका मतलब है कि डीबगर विधि को निष्पादित करने वाले सीएलआर होने का दिखावा करता है। स्पष्ट रूप से यदि कार्यान्वयन <code> रिटर्न "Hello" </ code> है, तो यह करना आसान है। यदि कार्यान्वयन P-Invoke करता है तो यह अधिक कठिन या असंभव है।
पैट्रिक नेल्सन - MSFT

3
@tzachs, एमुलेटर पूरी तरह से सिंगल थ्रेडेड है। यदि innerResultअशक्त के रूप में शुरू होता है, तो लूप कभी समाप्त नहीं होगा और अंततः मूल्यांकन का समय समाप्त हो जाएगा। वास्तव में, मूल्यांकन केवल प्रक्रिया में एक एकल थ्रेड को डिफ़ॉल्ट रूप से चलाने की अनुमति देता है, इसलिए आपको समान व्यवहार दिखाई देगा, भले ही एमुलेटर का उपयोग किया जाए या नहीं।
पैट्रिक नेल्सन - MSFT

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