2017-03-25 17 views
7

Tôi đang đọc một số báo cáo sự cố từ ứng dụng UWP (C#, được biên dịch với .NET Native) và tôi đang gặp khó khăn khi hiểu cú pháp/định dạng chính xác được sử dụng trong ngăn xếp ngăn xếp. Tôi đã thử tìm kiếm một số hướng dẫn trên internet nhưng tôi không nghĩ ra bất kỳ điều gì hữu ích.Làm thế nào để đọc/giải thích một dấu vết ngăn xếp C# thô chính xác?

Dưới đây là một vài ví dụ:

1)

MyProject.ViewModels.SomeViewModel.<OnLogin>d__69.MoveNext() 
  • OnLogin là tên của một phương pháp trong SomeViewModel, vậy tại sao là nó bên trong dấu ngoặc góc? Là "ClassName".<"MethodName>..." cách thông thường để chỉ ra một phương pháp dụ?
  • Tôi hiểu rằng trình biên dịch C# biến mỗi đoạn mã giữa các cuộc gọi await thành các phương thức ẩn danh và lên lịch cho chúng bằng cách tiếp tục, vì vậy tôi đoán rằng d__69 cho biết tiếp tục không đồng bộ bên trong phương thức hiện tại.
    • 'd' là gì?
    • Các số liệu đó có phải ngẫu nhiên không? Ý tôi là, phương pháp này không có 69 await cuộc gọi, vì vậy tôi đoán những con số đó không phải là tuần tự. Có thể tìm ra phần chính xác trong phương pháp gốc từ số đó trong dấu vết ngăn xếp không?
  • Phương pháp MoveNext() ở cuối là gì? Loại nó được gọi là gì?

2)

MyProject.UserControls.SomeControl.<.ctor>b__0_0 
  • Tôi biết rằng .ctor đứng cho các nhà xây dựng đối tượng, và nhìn vào đoạn code tôi phát hiện ra rằng b__0_0 đứng cho một event handler giấu tên nói thêm bên trong hàm tạo, như sau: SomeEvent += (s, e) => Foo();.
    • 'b' là gì?
    • Tại sao có hai số có dấu gạch dưới? Cái nào trong số chúng đề cập đến chỉ mục phương thức ẩn danh? Ý tôi là, đây là cái đầu tiên (chỉ số của nó là 0) nhưng có hai 0 ở đây. Nếu đó là lần thứ hai, tôi có có 0_1, 1_0 hoặc cái gì khác không?

3) tôi có phương pháp này:

// Returns a Task that immediately throws when awaited, as soon as the token is cancelled 
public static Task<T> GetWatchedTask<T>(this Task<T> awaitableTask, CancellationToken token) 
{ 
    return awaitableTask.ContinueWith(task => task.GetAwaiter().GetResult(), token); 
} 

Và tôi có chồng này dấu vết:

MyProject.Helpers.Extensions.TasksExtensions.<>c__3$1<System.__Canon>.<GetWatchedTask>b__3_0($Task$1<__Canon> task) 
  • Tại sao không thực hiện thứ hai tham số (the tok vi) xuất hiện trong chữ ký?
  • Tại sao loại $Task$1 được viết bằng ký tự '$'?Nó giống như một số loại chỉ báo giữ chỗ/mặt đất (như trong regex) sao cho nó cũng có thể được sử dụng ở những nơi khác? (Ý tôi là, tôi thấy rằng $1<System.__Canon> theo những gì tôi đoán là kiểu trả về phương thức)
    • Nếu phần đầu tiên đó là kiểu trả về phương thức, tại sao không có phương thức nào có kiểu trả về? Tôi có rất nhiều dấu vết ngăn xếp với phương thức trả về một giá trị, nhưng chúng không có chữ ký giống nhau.
  • Tất cả nghĩa là gì .<>c__3$1<System.__Canon> có nghĩa là gì? Từ $1 và tôi đoán đó sẽ là loại trả về Task<T>, nhưng phần nào là b__3_0, vì tôi không có cuộc gọi hoặc trình xử lý sự kiện không đồng bộ? Điều đó có nghĩa là một cái gì đó khác nhau trong trường hợp này?

4)

Windows.UI.Xaml.Media.SolidColorBrush..ctor($Color color) 
  • Tại sao các loại tham số bắt đầu với nhân vật '$'? nó đứng để làm gì?

5) tôi có phương pháp này khác:

public static async Task<ObservableCollection<SomeCustomClass>> LoadItemGroups(String parentId) 

Và stack trace này:

MyProject.SQLiteDatabase.SQLiteManager.<>c__DisplayClass142_3.<LoadGroups>b__3() 
  • gì mà c__DisplayClass142_3? Đó có phải là cách để chỉ ra kiểu trả về với một kiểu đơn chứ không phải là ba lớp riêng biệt (Task, ObservableCollection, SomeCustomClass)?
  • Một lần nữa, tại sao tôi có b__3 ở đây, trong các dấu vết ngăn xếp khác, nó đã sử dụng định dạng d_xxx để chỉ ra đoạn mã không đồng bộ?

Xin lỗi vì nhiều câu hỏi, tôi hy vọng bài đăng này cũng sẽ giúp các lập trình viên khác của UWP C#.

Cảm ơn bạn trước sự giúp đỡ của bạn!

EDIT: Câu hỏi này nên không được coi là một bản sao của this other questions vì:

  • Nó trình bày trường hợp khác nhau (phương pháp xây dựng, các loại generic cú pháp vv ..) thay vì chỉ yêu cầu ý nghĩa của một số từ khóa/ký hiệu mặc định có liên quan đến một loại biến nhất định
  • Cụ thể yêu cầu cách so sánh dấu vết ngăn xếp cho trước với chữ ký phương thức gốc và các bước cần thực hiện để đạt được điều đó
  • Nó trình bày các ví dụ khác nhau trong các ngữ cảnh khác nhau, thay vì chỉ hỏi một câu hỏi chung
  • Và bằng cách này, làm thế nào có thể "tên trình gỡ lỗi VS debugger" thậm chí được coi là một tiêu đề câu hỏi thích hợp? Làm thế nào là một người dùng khác phải tìm câu hỏi đó khi tìm kiếm các dấu vết dấu vết C# ngăn xếp có nghĩa là gì?

Trả lời

5

Tôi đặt cược Eric Lippert sẽ đến sau và đưa ra câu trả lời đúng hơn, nhưng trong trường hợp đó sẽ không xảy ra - đây là của tôi, bởi vì tôi cũng quan tâm đến điều này. Ý nghĩa của "d", "c" và các biểu tượng tương tự tôi nhận được từ câu trả lời củacủa Eric Lippert.

1) MyProject.ViewModels.SomeViewModel.<OnLogin>d__69.MoveNext()

Cái này là tương đối đơn giản. OnLogin là phương pháp không đồng bộ, và các phương thức như vậy được viết lại bởi trình biên dịch vào một máy trạng thái. Máy trạng thái này triển khai giao diện IAsyncStateMachine có phương thức MoveNext. Vì vậy, phương thức async của bạn về cơ bản trở thành một chuỗi các lời gọi MoveNext của máy trạng thái đó. Đó là lý do tại sao bạn thấy MoveNext() trong theo dõi ngăn xếp.

MyProject.ViewModels.SomeViewModel.<OnLogin>d__69 là tên của lớp máy trạng thái được tạo. Vì máy trạng thái này có liên quan đến phương thức OnLogin - nó trở thành một phần của tên kiểu. d là "lớp lặp" bằng liên kết ở trên. Lưu ý rằng thông tin từ liên kết ở trên là 7 tuổi và trước async \ đang chờ triển khai, nhưng tôi đoán rằng máy trạng thái tương tự như trình lặp (cùng phương thức MoveNext, cùng nguyên tắc) - vì vậy "lớp lặp" có vẻ ổn. 69 là một số số duy nhất \ truy cập. Tôi đoán nó chỉ là truy cập, bởi vì nếu tôi biên dịch dll chỉ với hai phương pháp async - máy nhà nước của họ sẽ là d__0d__1. Không thể suy ra phần nào của phương thức async đã ném dựa trên thông tin này.

2) b là "phương thức ẩn danh" (liên kết ở trên). Tôi đã thực hiện một số thử nghiệm và tôi nghĩ chỉ mục đầu tiên có liên quan đến phương pháp trong đó phương pháp ẩn danh được sử dụng và chỉ mục thứ hai dường như liên quan đến chỉ mục của phương thức ẩn danh bên trong phương thức mà chúng được sử dụng. Ví dụ giả sử bạn sử dụng 2 phương thức nặc danh trong hàm tạo và 2 phương thức nặc danh trong phương thức Foo trong cùng một lớp. Sau đó:

public Test() { 
    Handler += (s, e) => Foo(); // this will be `b__0_0` because it's first in this method 
    Handler += (s, e) => Bar(); // this will be `b__0_1` because it's second 
} 

static void Foo() { 
    Action a =() => Console.WriteLine("test"); // this is `b__1_0`, 1 refers to it being in another method, not in constructor. 
    // if we use anonymous method in `Bar()` - it will have this index 2 
    a(); 
    Action b =() => Console.WriteLine("test2"); // this is `b__1_1` 
    b(); 
} 

3) Điều này có vẻ khá phức tạp. Trước tiên, bạn hỏi "Tại sao thông số thứ hai (mã thông báo) hiển thị trong chữ ký". Điều đó đơn giản - bởi vì phương thức được đề cập đại diện cho phương thức ẩn danh task => task.GetAwaiter().GetResult(), không phải phương thức GetWatchedTask của bạn. Bây giờ tôi đã không thể tái tạo dấu vết ngăn xếp của bạn với dấu vết này, nhưng vẫn còn một số thông tin. Đầu tiên, System.__Canon là:

Phương thức nội bộ được sử dụng để khởi tạo "chuẩn" phương pháp có thể cho các cảnh báo chung. Tên "__Canon" sẽ không bao giờ được nhìn thấy bởi người dùng nhưng nó sẽ xuất hiện rất nhiều trong dấu vết ngăn xếp của trình gỡ lỗi liên quan đến generics vì vậy nó được giữ cố tình ngắn để tránh gây phiền toái.

Có vẻ khó hiểu đối với tôi, nhưng tôi đoán loại này đại diện cho số T của bạn trong thời gian chạy. Sau đó, <>c__3$1<System.__Canon><>c__3$1<T> và là tên của trình biên dịch tạo ra lớp, trong đó "c" là "lớp đóng gói phương thức ẩn danh" (từ liên kết ở trên). Lớp như vậy được tạo ra bởi trình biên dịch khi bạn tạo một đóng, vì vậy hãy nắm bắt một số trạng thái bên ngoài trong phương thức ẩn danh của bạn. Những gì đã bị bắt nên được lưu trữ ở đâu đó, và nó được lưu trữ trong lớp đó.

Đi xa hơn, <GetWatchedTask>b__3_0 là một phương pháp trong lớp ẩn danh ở trên. Nó đại diện cho phương pháp task => task.GetAwaiter().GetResult() của bạn. Mọi thứ từ điểm 2 cũng áp dụng ở đây.

Tôi không biết ý nghĩa của $, có thể nó đại diện cho số tham số loại.Vì vậy, có thể Task$1<System.__Canon> có nghĩa là Task<T> và một cái gì đó như Tuple$2<System.__Canon có nghĩa là Tuple<T1, T2>.

4) Thật không may là tôi không biết và không thể tái tạo.

5) c__DisplayClass142_3 là lớp đóng lại (xem điểm 3). <LoadGroups>b__3() là phương pháp ẩn danh bạn đã sử dụng trong phương thức LoadGroups. Vì vậy, đó chỉ ra một số phương pháp vô danh mà là đóng cửa (bắt bên ngoài nhà nước) và được gọi là trong phương pháp LoadGroups.

+0

Cảm ơn người đàn ông, trông giống như một câu trả lời tuyệt vời cho tôi! Vẫn mong chờ một câu trả lời khác của Eric Lippert, vì nó có thể thêm một số chi tiết cho chắc chắn. – Sergio0694

+0

@ Sergio0694 hy vọng bạn biết ít nhất có thể tìm ra phương pháp chính xác được đề cập trong ngăn xếp dấu vết. Nhân tiện, bạn có một mã để tái tạo những dấu hiệu $ trong stacktrace? Đặc biệt là ở điểm thứ 4 của bạn. – Evk

Các vấn đề liên quan