Visual Studio hata ayıklayıcısının bir ToString geçersiz kılma değerlendirmesini durdurmasını sağlayan nedir?


221

Çevre: Visual Studio 2015 RTM. (Eski sürümleri denemedim.)

Son zamanlarda, Noda Time kodumdan bazılarının hatalarını ayıkladım ve yerel bir değişken NodaTime.Instant( structNoda Time'daki merkezi türlerden biri), "Locals" ve "Watch" pencerelerini aldığımda fark ettim. ToString()geçersiz kılma olarak görünmüyor . ToString()İzleme penceresinde açıkça çağırırsam , uygun temsili görüyorum, ancak aksi takdirde sadece görüyorum:

variableName       {NodaTime.Instant}

ki bu pek kullanışlı değil.

Ben sabit dize döndürmek için geçersiz kılma değiştirirseniz, dize olduğunu açıkça orada olduğunu toplayabildiğinde yüzden, ayıklayıcısında görüntülenen - bu sadece kendi "normal" durumda kullanmak istemiyor.

Bunu küçük bir demo uygulamasında yerel olarak çoğaltmaya karar verdim ve işte ne buldum. (Bu yazının erken bir versiyonunda DemoStructbir sınıf olduğunu ve DemoClasshiç var olmadığını unutmayın - benim hatam, ama şimdi garip görünen bazı yorumları açıklıyor ...)

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();
    }
}

Hata ayıklayıcıda, şimdi görüyorum:

demoClass    {DemoClass}
demoStruct   {Struct: Bar}

Ancak, Thread.Sleepçağrıyı 1 saniyeden 900 ms'ye düşürürsem, hala kısa bir duraklama olur, ancak sonra Class: Foodeğer olarak görüyorum . Thread.SleepÇağrının ne kadar sürdüğü önemli değil DemoStruct.ToString(), her zaman düzgün görüntüleniyor - ve hata ayıklayıcı, uyku tamamlanmadan önce değeri görüntüler. (Sanki Thread.Sleepdevre dışı gibi.)

Şimdi Instant.ToString()Noda Time'da adil bir miktar iş var, ama kesinlikle bir saniye sürmez - bu yüzden muhtemelen hata ayıklayıcının bir ToString()çağrıyı değerlendirmekten vazgeçmesine neden olan daha fazla koşul vardır . Ve elbette bu bir yapı.

Bir yığın sınırı olup olmadığını görmek için yinelenen denedim, ama durum böyle değil.

Öyleyse, VS'nin tam olarak değerlendirilmesini engelleyen şeyi nasıl çözebilirim Instant.ToString()? Aşağıda belirtildiği gibi, DebuggerDisplayAttributeyardımcı gibi görünüyor, ancak nedenini bilmeden , ihtiyaç duyduğumda ve ihtiyaç duymadığım zaman asla tamamen emin olmayacağım.

Güncelleme

Eğer kullanırsam DebuggerDisplayAttributeişler değişir:

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

bana verir:

demoClass      Evaluation timed out

Oysa Noda Time'a uyguladığımda:

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

basit bir test uygulaması bana doğru sonucu gösterir:

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

Bu yüzden muhtemelen Noda Time'daki problem, zaman aşımlarından geçmese bile DebuggerDisplayAttribute , zorlayan bir durumdur . (Bu, Instant.ToStringzaman aşımından kaçınmak için yeterince hızlı olan beklentim doğrultusunda olacaktır .)

Bu olabilir yeterince iyi bir çözüm olabilir - ama yine de böyle neler biliyorum ve Noda Zamanında bütün değişik değer türleri üzerinde niteliğini koymak zorunda kalmamak için sadece kodunu değiştirmek veya reddetmek için istiyorum.

Meraklı ve meraklı

Hata ayıklayıcının kafasını karıştıran ne varsa, sadece bazen karıştırır. En bir sınıf oluşturalım tutan bir Instantve kendi için kullanır ToString()yöntemle:

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();
    }
}

Şimdi görüyorum:

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

Ancak, yorumlarda Eren'in önerisinde, InstantWrapperbir yapı olarak değişirsem , şunu elde ederim:

instant    {NodaTime.Instant}
wrapper    {InstantWrapper}

O yüzden olabilir değerlendirmek Instant.ToString()- bu kadar uzun başka bir kişinin çağrılan olarak ToStringbir sınıf içinde yöntemle .... Class / struct kısmı, sonucu elde etmek için hangi kodun yürütülmesi gerektiğine değil, görüntülenen değişkenin türüne göre önemli görünüyor.

Bunun başka bir örneği olarak, eğer kullanırsak:

object boxed = NodaConstants.UnixEpoch;

... sonra düzgün çalışıyor, doğru değeri gösteriyor. Renk karıştı beni.


7
@ VS 2013'te aynı davranışı (c # 6 öğelerini kaldırmak zorunda kaldım), ek bir mesajla: Name İşlev değerlendirmesi devre dışı bırakıldı çünkü önceki bir işlev değerlendirmesi zaman aşımına uğradı. İşlev değerlendirmesini yeniden etkinleştirmek için yürütmeye devam etmelisiniz. string
vc 74

1
c # 6.0'ya hoş geldiniz @ 3-14159265358979323846264
Neel

1
Belki DebuggerDisplayAttributebiraz denemek için neden olur.
Rawling

1
bkz. 5. nokta neelbhatt40.wordpress.com/2015/07/13/… @ 3-14159265358979323846264 için yeni c # 6.0
Neel

5
@DiomidisSpinellis: Peki burada sordum ki a) Daha önce aynı şeyi gören ya da VS'nin içini bilen biri cevap verebilir; b) gelecekte aynı sorunla karşılaşan herkes cevabı hızlı bir şekilde alabilir.
Jon Skeet

Yanıtlar:


193

Güncelleme:

Bu hata, Visual Studio 2015 Güncelleştirme 2'de giderilmiştir. Güncelleştirme 2 veya sonraki bir sürümünü kullanarak yapı değerleri üzerinde ToString'i değerlendirmekte hala sorun yaşıyorsanız bana bildirin.

Orijinal Yanıt:

Visual Studio 2015 ile bilinen bir hata / tasarım sınırlamasıyla karşılaşıyorsunuz ve yapı türlerinde ToString'i çağırıyorsunuz. Bu, uğraşırken de gözlemlenebilir System.DateTimeSpan. System.DateTimeSpan.ToString()Visual Studio 2013 ile değerlendirme pencerelerinde çalışır, ancak 2015'te her zaman çalışmaz.

Düşük düzeyli ayrıntılarla ilgileniyorsanız, neler oluyor:

Değerlendirmek için ToString, hata ayıklayıcı "işlev değerlendirme" olarak bilinen şeyi yapar. Büyük ölçüde basitleştirilmiş terimlerle, hata ayıklayıcı, geçerli iş parçacığı dışındaki işlemdeki tüm iş parçacıklarını askıya alır, geçerli iş parçacığının içeriğini ToStringişleve değiştirir, gizli bir koruma kesme noktası ayarlar ve işlemin devam etmesine izin verir. Koruma kesme noktasına çarpıldığında, hata ayıklayıcı işlemi önceki durumuna geri yükler ve pencereyi doldurmak için işlevin dönüş değeri kullanılır.

Lambda ifadelerini desteklemek için CLR İfade Değerlendiricisini Visual Studio 2015'te tamamen yeniden yazmak zorunda kaldık. Üst düzeyde uygulama:

  1. Roslyn, çeşitli denetim pencerelerinde görüntülenecek değerleri almak için ifadeler / yerel değişkenler için MSIL kodu üretir.
  2. Hata ayıklayıcı, sonucu almak için IL'yi yorumlar.
  3. Herhangi bir "çağrı" talimatı varsa, hata ayıklayıcı yukarıda açıklandığı gibi bir işlev değerlendirmesi yürütür.
  4. Hata ayıklayıcı / roslyn bu sonucu alır ve kullanıcıya gösterilen ağaç görünümünde biçimlendirir.

IL'nin yürütülmesi nedeniyle, hata ayıklayıcı her zaman karmaşık "gerçek" ve "sahte" değerlerin bir karışımıyla uğraşmaktadır. Hata ayıklanan süreçte gerçek değerler vardır. Sahte değerler yalnızca hata ayıklayıcı işleminde bulunur. Uygun yapı semantiğini uygulamak için, hata ayıklayıcının IL yığınına bir yapı değeri iletirken daima değerin bir kopyasını alması gerekir. Kopyalanan değer artık "gerçek" bir değer değildir ve artık yalnızca hata ayıklayıcı işleminde bulunmaktadır. Bu, daha sonra işlev değerlendirmesi yapmamız gerektiğinde ToString, işlemde değer olmadığından yapamayız demektir. Değeri denemek ve elde etmek için,ToStringyöntem. Bazı şeyleri taklit edebiliriz, ancak birçok sınırlama var. Örneğin, yerel kodu taklit edemeyiz ve "gerçek" delege değerlerine veya yansıma değerleriyle ilgili çağrılara çağrı yapamayız.

Tüm bunları göz önünde bulundurarak, gördüğünüz çeşitli davranışlara neden olan şey:

  1. Hata ayıklayıcı değerlendirmiyor NodaTime.Instant.ToString-> Bunun nedeni yapı türü olması ve ToString uygulamasının yukarıda açıklanan hata ayıklayıcı tarafından taklit edilememesidir.
  2. Thread.SleepToStringBir yapı üzerinde çağrıldığında sıfır zaman alıyor gibi görünüyor -> Bunun nedeni emülatörün yürütmesidir ToString. Thread.Sleep yerel bir yöntemdir, ancak emülatör bunun farkındadır ve aramayı görmezden gelir. Bunu kullanıcıya göstermek için bir değer elde etmek için yapıyoruz. Bu durumda bir gecikme yardımcı olmaz.
  3. DisplayAttibute("ToString()")İşler. -> Bu kafa karıştırıcı. Zımni çağrı arasındaki tek fark ToStringve DebuggerDisplayörtük herhangi zaman aşımları olmasıdır ToString değerlendirme tüm örtülü devre dışı bırakır ToStringsonraki ayıklama oturumu kadar bu tür değerlendirmeler. Bu davranışı gözlemliyor olabilirsiniz.

Tasarım sorunu / hata açısından, bu, Visual Studio'nun gelecekteki bir sürümünde ele almayı planladığımız bir şeydir.

Umarım bu şeyleri temizler. Eğer başka sorunuz varsa bana haber verin. :-)


1
Uygulama sadece "bir dizgi değişmezi döndür" ise Instant.ToString nasıl çalışır? Hala açıklanamayan bazı karmaşıklıklar var gibi görünüyor :) Bu davranışı gerçekten yeniden üretebileceğimi kontrol edeceğim ...
Jon Skeet

1
@Jon, ne istediğinden emin değilim. Hata ayıklayıcı, gerçek işlev değerlendirmesi yaparken uygulamadan bağımsızdır ve her zaman önce bunu dener. Hata ayıklayıcı, yalnızca çağrıyı taklit etmesi gerektiğinde uygulamayı önemser - Bir dizgi değişmezini döndürmek taklit edilmesi en basit durumdur.
Patrick Nelson - MSFT

8
İdeal olarak CLR'nin her şeyi yürütmesini istiyoruz. Bu, en doğru ve güvenilir sonuçları sağlar. Bu yüzden ToString çağrıları için gerçek fonksiyon değerlendirmesi yapıyoruz. Bu mümkün olmadığında, çağrıyı taklit etmeye geri döneriz. Bu, hata ayıklayıcının, yöntemi uygulayan CLR gibi davrandığı anlamına gelir. Açıkçası uygulama <code> "Merhaba" </code> döndürüyorsa, bunu yapmak kolaydır. Uygulama bir P-Invoke yaparsa daha zor veya imkansızdır.
Patrick Nelson - MSFT

3
@tzachs, Emülatör tamamen tek dişli. Eğer innerResultbaşlar boş olarak yola, döngü sona asla ve sonunda değerlendirme zaman aşımına uğrar. Aslında, değerlendirmeler işlemdeki tek bir iş parçacığının varsayılan olarak çalışmasına izin verir, bu nedenle öykünücünün kullanılıp kullanılmamasından bağımsız olarak aynı davranışı görürsünüz.
Patrick Nelson - MSFT

2
BTW, değerlendirmelerinizin birden çok iş parçacığı gerektirdiğini biliyorsanız Debugger.NotifyOfCrossThreadDependency sayfasına bakın . Bu yöntemin çağrılması, değerlendirmenin tüm iş parçacıklarının çalışmasını gerektirdiğini belirten bir mesajla değerlendirmeyi iptal eder ve hata ayıklayıcı, kullanıcının değerlendirmeyi zorlamak için basabileceği bir düğme sağlar. Dezavantajı, değerlendirme sırasında diğer evrelerde vurulan kesme noktaları göz ardı edilecektir.
Patrick Nelson - MSFT
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.