在更新我的UI代码(.NET 4.0应用程序中的C#)时,由于对UI的调用在错误的线程中执行,我遇到了奇怪的崩溃。但是,我已经在主线程上调用了该调用,因此崩溃没有任何意义:MainThreadDispatcher.Invoke(new Action(View.Method))崩溃,原因是“调用线程无法访问该对象,因为另一个线程拥有它。”在“ View ”属性上。

经过进一步调查,我找到了原因:我正在通过方法组进行调用。我以为使用方法组或委托(delegate)/ lambda本质上是同一件事(另请参见this questionthis 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/

10-10 18:29