Môi trường: Visual Studio 2015 RTM. (Tôi chưa thử các phiên bản cũ hơn.)
Gần đây, tôi đã gỡ lỗi một số mã Thời gian Noda của mình và tôi nhận thấy rằng khi tôi có một loại biến cục bộ NodaTime.Instant
(một trong những struct
loại trung tâm trong Giờ Noda), cửa sổ "Địa phương" và "Xem" không xuất hiện để gọi ToString()
ghi đè của nó . Nếu tôi gọi ToString()
một cách rõ ràng trong cửa sổ đồng hồ, tôi thấy đại diện thích hợp, nhưng nếu không thì tôi chỉ thấy:
variableName {NodaTime.Instant}
Điều này không hữu ích lắm.
Nếu tôi thay đổi ghi đè để trả về một chuỗi không đổi, chuỗi đó sẽ được hiển thị trong trình gỡ lỗi, vì vậy rõ ràng có thể chọn nó ở đó - nó chỉ không muốn sử dụng nó ở trạng thái "bình thường".
Tôi đã quyết định sao chép nó cục bộ trong một ứng dụng demo nhỏ và đây là những gì tôi nghĩ ra. (Lưu ý rằng trong phiên bản đầu của bài đăng này, DemoStruct
là một lớp và DemoClass
hoàn toàn không tồn tại - lỗi của tôi, nhưng nó giải thích một số nhận xét trông kỳ lạ bây giờ ...)
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();
}
}
Trong trình gỡ lỗi, bây giờ tôi thấy:
demoClass {DemoClass}
demoStruct {Struct: Bar}
Tuy nhiên, nếu tôi giảm Thread.Sleep
cuộc gọi xuống từ 1 giây xuống còn 900ms, vẫn có một khoảng dừng ngắn, nhưng sau đó tôi thấy đó Class: Foo
là giá trị. Nó dường như không quan trọng Thread.Sleep
cuộc gọi kéo dài bao lâu DemoStruct.ToString()
, nó luôn được hiển thị đúng - và trình gỡ lỗi hiển thị giá trị trước khi giấc ngủ hoàn thành. (Như thể Thread.Sleep
là bị vô hiệu hóa.)
Bây giờ Instant.ToString()
trong Noda Time thực hiện một số lượng công việc khá lớn, nhưng chắc chắn nó không mất cả giây - vì vậy có lẽ có nhiều điều kiện khiến trình gỡ lỗi từ bỏ việc đánh giá ToString()
cuộc gọi. Và tất nhiên đó là một cấu trúc nào.
Tôi đã thử đệ quy để xem liệu đó có phải là giới hạn ngăn xếp hay không, nhưng dường như không phải vậy.
Vì vậy, làm thế nào tôi có thể tìm ra những gì ngăn chặn VS đánh giá đầy đủ Instant.ToString()
? Như đã lưu ý dưới đây, có DebuggerDisplayAttribute
vẻ hữu ích, nhưng không hiểu tại sao , tôi sẽ không bao giờ hoàn toàn tự tin khi tôi cần và khi tôi không.
Cập nhật
Nếu tôi sử dụng DebuggerDisplayAttribute
, mọi thứ sẽ thay đổi:
// For the sample code in the question...
[DebuggerDisplay("{ToString()}")]
public class DemoClass
đưa cho tôi:
demoClass Evaluation timed out
Trong khi tôi áp dụng nó trong Noda Time:
[DebuggerDisplay("{ToString()}")]
public struct Instant
một ứng dụng thử nghiệm đơn giản cho tôi thấy kết quả đúng:
instant "1970-01-01T00:00:00Z"
Vì vậy, có lẽ vấn đề trong Noda Thời gian là một số điều kiện là DebuggerDisplayAttribute
không có hiệu lực thông qua - mặc dù nó không buộc qua timeout. (Điều này sẽ phù hợp với kỳ vọng của tôi, Instant.ToString
đủ nhanh để tránh thời gian chờ.)
Đây có thể là một giải pháp đủ tốt - nhưng tôi vẫn muốn biết chuyện gì đang xảy ra và liệu tôi có thể thay đổi mã đơn giản để tránh phải đặt thuộc tính trên tất cả các loại giá trị khác nhau trong Noda Time hay không.
Tò mò và tò mò
Bất cứ điều gì gây nhầm lẫn trình gỡ lỗi chỉ đôi khi nhầm lẫn nó. Hãy tạo ra một lớp mà giữ một Instant
và sử dụng nó cho riêng mình ToString()
phương pháp:
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();
}
}
Bây giờ tôi cuối cùng thấy:
instant {NodaTime.Instant}
wrapper {1970-01-01T00:00:00Z}
Tuy nhiên, theo gợi ý của Eren trong các bình luận, nếu tôi thay đổi InstantWrapper
thành một cấu trúc, tôi nhận được:
instant {NodaTime.Instant}
wrapper {InstantWrapper}
Vì vậy, nó có thể đánh giá Instant.ToString()
- miễn là điều đó được gọi bằng một ToString
phương thức khác ... nằm trong một lớp. Phần lớp / struct dường như rất quan trọng dựa trên loại biến được hiển thị, chứ không phải mã nào cần được thực thi để có kết quả.
Một ví dụ khác về điều này, nếu chúng ta sử dụng:
object boxed = NodaConstants.UnixEpoch;
... Sau đó, nó hoạt động tốt, hiển thị đúng giá trị. Màu tôi bối rối.
DebuggerDisplayAttribute
sẽ khiến nó cố gắng hơn một chút.