2010-06-24 26 views
14

Đây là ví dụ giả định của tôi. Tôi có một cửa sổ WPF rất đơn giản với một nút. Sự kiện Button.Click có trình xử lý giống như thế này.Tại sao ngoại lệ không được tuyên truyền bởi WPF Dispatcher.Invoke?

Action doit =() => 
{ 
    Action error =() => { throw new InvalidOperationException("test"); }; 

    try { 
     this.Dispatcher.Invoke(error, DispatcherPriority.Normal); 
    } catch (Exception ex) { 
     System.Diagnostics.Trace.WriteLine(ex); 
     throw; 
    } 
}; 
doit.BeginInvoke(null, null); 

Tôi cho rằng ngoại lệ sẽ bị bắt và ghi lại bằng lệnh gọi Trace.WriteLine. Thay vào đó, không có ngoại lệ nào bị bắt và ứng dụng bị hỏng.

Có ai biết giải thích nào có thể xảy ra không? Và bạn đề nghị cách giải quyết nào để bắt các trường hợp ngoại lệ được gửi bởi đại biểu được gọi bởi Dispatcher.Invoke?

Cập nhật 1: Tôi đặt throw trong mã xử lý ngoại lệ. Tôi không muốn thực sự bỏ qua ngoại lệ. Toàn bộ vấn đề của câu hỏi của tôi là xử lý nó một cách chính xác. Vấn đề là mã xử lý ngoại lệ không bao giờ được thực thi.

Hãy nhớ rằng đây là một ví dụ giả định. Mã thực sự của tôi không giống như thế. Ngoài ra, giả sử rằng tôi không thể thay đổi mã trong phương thức được gọi.

Cập nhật 2: Hãy xem xét ví dụ tương tự này. Thay vì một cửa sổ WPF, tôi có một cửa sổ Windows Forms. Nó có một nút với gần như chính xác cùng một xử lý. Sự khác biệt duy nhất là trong mã yêu cầu. Nó như thế này

this.Invoke(error); 

Trong Windows Forms, mã xử lý ngoại lệ được thực thi. Tại sao sự khác biệt?

Trả lời

5

CẬP NHẬT: Tuân thủ các ngoại lệ trong các chủ đề khác, bạn muốn sử dụng một Task, xếp hàng nó để thread Dispatcher (sử dụng TaskScheduler.FromCurrentSynchronizationContext), và chờ đợi vào nó, như vậy:

var ui = TaskScheduler.FromCurrentSynchronizationContext(); 
Action doit =() => 
{ 
    var error = Task.Factory.StartNew(
     () => { throw new InvalidOperationException("test"); }, 
     CancellationToken.None, 
     TaskCreationOptions.None, 
     ui); 

    try { 
     error.Wait(); 
    } catch (Exception ex) { 
     System.Diagnostics.Trace.WriteLine(ex); 
    } 
}; 
doit.BeginInvoke(null, null); 

CẬP NHẬT (lại): Vì mục tiêu của bạn là thành phần có thể tái sử dụng, tôi khuyên bạn nên chuyển sang giao diện dựa trên Task hoặc cái gì khác dựa trên SynchronizationContext chẳng hạn như event-based asynchronous pattern, thay vì đặt thành phần trên Dispatcher hoặc ISynchronizeInvoke.

Dispatcher thành phần dựa trên chỉ hoạt động trên WPF/Silverlight; ISynchronizeInvoke thành phần dựa trên chỉ hoạt động trên Windows Forms. SynchronizationContext thành phần dựa trên sẽ làm việc với WPF hoặc Windows Forms minh bạch, và (với một chút công việc) ASP.NET, giao diện điều khiển ứng dụng, cửa sổ dịch vụ, vv

Mô hình không đồng bộ dựa trên sự kiện là cách đề nghị cũ của văn bản SynchronizationContext thành phần dựa trên; nó vẫn còn xung quanh mã .NET 3.5. Tuy nhiên, nếu bạn đang ở trên .NET 4, thư viện song song nhiệm vụ linh hoạt hơn, sạch sẽ và mạnh mẽ hơn. TaskScheduler.FromCurrentSynchronizationContext sử dụng SynchronizationContext bên dưới và là cách mới để viết các thành phần có thể tái sử dụng cần loại đồng bộ hóa này.

+0

Cách tôi xử lý ngoại lệ bị bắt là không liên quan. Tôi có thể đặt một số đăng nhập hoặc bất cứ điều gì ở đó. Vấn đề là mã xử lý ngoại lệ không bao giờ được thực thi. – jpbochi

+0

Đó là vì ngoại lệ được ném vào một luồng khác - luồng 'Dispatcher'. –

+1

@Sthepen: Tôi biết điều đó. Tôi đã mong đợi để có được một TargetInvocationException hoặc một cái gì đó như nó. – jpbochi

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