Trong khi cập nhật mã giao diện người dùng của tôi (C# trong ứng dụng .NET 4.0), tôi gặp phải sự cố lạ do cuộc gọi đến giao diện người dùng đang được thực hiện sai chuỗi. Tuy nhiên, tôi đã gọi lời gọi đó trên luồng chính, vì vậy sự cố không có ý nghĩa: MainThreadDispatcher.Invoke(new Action(View.Method))
bị lỗi với "Chuỗi cuộc gọi không thể truy cập đối tượng này vì một chuỗi khác sở hữu nó." trên thuộc tính Chế độ xem.Sử dụng nhóm phương pháp C# thực hiện mã
Khi điều tra thêm, tôi đã tìm ra nguyên nhân: Tôi đã gọi qua nhóm phương pháp. Tôi đã nghĩ rằng sử dụng một nhóm phương pháp hoặc một đại biểu/lambda về cơ bản là giống nhau (xem thêm this question và this question). Thay vào đó, việc chuyển đổi nhóm phương thức thành một đại biểu làm cho mã thực thi, kiểm tra giá trị của View
. Việc này được thực hiện ngay lập tức, tức là trên luồng ban đầu (không phải giao diện người dùng), gây ra sự cố. Nếu tôi sử dụng một lambda thay vào đó, kiểm tra tài sản được thực hiện sau đó, và do đó trong các chủ đề chính xác.
Điều đó có vẻ thú vị, để nói rằng ít nhất. Có bất kỳ nơi nào trong tiêu chuẩn C#, nơi điều này được đề cập không? Hoặc là tiềm ẩn do cần phải tìm chuyển đổi chính xác?
Đây là chương trình thử nghiệm. Đầu tiên, cách trực tiếp. Thứ hai, trong hai bước, trong đó tốt hơn cho thấy những gì sẽ xảy ra. Để có thêm niềm vui, tôi sau đó sửa đổi Item
sau khi đại biểu đã được tạo.
namespace ConsoleApplication1 // Add a reference to WindowsBase to a standard ConsoleApplication
{
using System.Threading;
using System.Windows.Threading;
using System;
static class Program
{
static Dispatcher mainDispatcher;
static void Main()
{
mainDispatcher = Dispatcher.CurrentDispatcher;
mainDispatcher.Thread.Name = "Main thread";
var childThread = new Thread(() =>
{
Console.WriteLine("--- Method group ---");
mainDispatcher.Invoke(new Action(Item.DoSomething));
Console.WriteLine("\n--- Lambda ---");
mainDispatcher.Invoke(new Action(() => Item.DoSomething()));
Console.WriteLine("\n--- Method group (two steps) ---");
var action = new Action(Item.DoSomething);
Console.WriteLine("Invoking");
mainDispatcher.Invoke(action);
Console.WriteLine("\n--- Lambda (two steps) ---");
action = new Action(() => Item.DoSomething());
Console.WriteLine("Invoking");
mainDispatcher.Invoke(action);
Console.WriteLine("\n--- Method group (modifying Item) ---");
action = new Action(Item.DoSomething);
item = null;
mainDispatcher.Invoke(action);
item = new UIItem();
Console.WriteLine("\n--- Lambda (modifying Item) ---");
action = new Action(() => Item.DoSomething());
item = null;
Console.WriteLine("Invoking");
mainDispatcher.Invoke(action);
mainDispatcher.InvokeShutdown();
});
childThread.Name = "Child thread";
childThread.Start();
Dispatcher.Run();
}
static UIItem item = new UIItem();
static UIItem Item
{
get
{
// mainDispatcher.VerifyAccess(); // Uncomment for crash.
Console.WriteLine("UIItem: In thread: {0}", Dispatcher.CurrentDispatcher.Thread.Name);
return item;
}
}
private class UIItem
{
public void DoSomething()
{
Console.WriteLine("DoSomething: In thread: {0}", Dispatcher.CurrentDispatcher.Thread.Name);
}
}
}
}
phiên bản ngắn:
namespace ConsoleApplication1 // Add a reference to WindowsBase to a standard ConsoleApplication
{
using System.Threading;
using System.Windows.Threading;
using System;
static class Program
{
static Dispatcher mainDispatcher;
static void Main()
{
mainDispatcher = Dispatcher.CurrentDispatcher;
mainDispatcher.Thread.Name = "Main thread";
var childThread = new Thread(() =>
{
Console.WriteLine("--- Method group ---");
mainDispatcher.Invoke(new Action(Item.DoSomething));
Console.WriteLine("\n--- Lambda ---");
mainDispatcher.Invoke(new Action(() => Item.DoSomething()));
mainDispatcher.InvokeShutdown();
});
childThread.Name = "Child thread";
childThread.Start();
Dispatcher.Run();
}
static UIItem item = new UIItem();
static UIItem Item
{
get
{
mainDispatcher.VerifyAccess();
Console.WriteLine("UIItem: In thread: {0}", Dispatcher.CurrentDispatcher.Thread.Name);
return item;
}
}
private class UIItem
{
public void DoSomething()
{
Console.WriteLine("DoSomething: In thread: {0}", Dispatcher.CurrentDispatcher.Thread.Name);
}
}
}
}
Cuộc gọi nào không thành công? Ngăn xếp cuộc gọi là gì? – SLaks
Loại bỏ dòng bình luận bằng VerifyAccess(), và bạn sẽ thấy rằng tất cả các cuộc gọi sử dụng các nhóm phương thức đều thất bại, vì thuộc tính Item được truy cập trên chuỗi con. –
Một chương trình ngắn nhưng đầy đủ * chỉ * hiển thị cuộc gọi có vấn đề sẽ thực sự hữu ích. –