在更新我的UI代码(.NET 4.0应用程序中的C#)时,由于对UI的调用在错误的线程中执行,我遇到了奇怪的崩溃。但是,我已经在主线程上调用了该调用,因此崩溃没有任何意义:MainThreadDispatcher.Invoke(new Action(View.Method))
崩溃,原因是“调用线程无法访问该对象,因为另一个线程拥有它。”在“ View ”属性上。
经过进一步调查,我找到了原因:我正在通过方法组进行调用。我以为使用方法组或委托(delegate)/ lambda本质上是同一件事(另请参见this question和this question)。相反,将方法组转换为委托(delegate)会使代码执行,并检查View
的值。这是立即完成的,即在导致崩溃的原始(非UI)线程上。如果我改用lambda,则稍后会在正确的线程中检查属性。
至少可以这样说,这似乎很有趣。 在C#标准中是否有提及的地方?还是由于需要找到正确的转换而隐含?
这是一个测试程序。首先,直接的方式。其次,分两个步骤,可以更好地显示发生了什么。为了获得更多乐趣,我然后在创建委托(delegate)之后修改Item
。
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);
}
}
}
}
精简版:
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);
}
}
}
}
最佳答案
急切地访问该属性的事实在方法组成员中并不特别。通常,这是成员表达式的特征。
实际上,正是lambda导致了特例:它的主体(以及属性访问)将被推迟,直到委托(delegate)被实际执行为止。
从规格:
关于c# - 使用C#方法组执行代码,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/8329026/