使用c#方法组执行代码

本文关键字:执行 代码 方法 使用 | 更新日期: 2023-09-27 18:16:08

在更新我的UI代码时(c#在。net 4.0应用程序中),由于在错误的线程中执行对UI的调用,我遇到了一个奇怪的崩溃。然而,我已经在主线程上调用了这个调用,所以崩溃是没有意义的:MainThreadDispatcher.Invoke(new Action(View.Method))在视图属性上崩溃了,"调用线程不能访问这个对象,因为一个不同的线程拥有它。"

经过进一步调查,我找到了原因:我通过一个方法组调用。我曾认为使用方法组或委托/lambda本质上是相同的事情(参见这个问题和这个问题)。相反,将方法组转换为委托会导致执行代码,检查View的值。这是立即完成的,即在导致崩溃的原始(非ui)线程上执行。如果我使用lambda,检查属性是稍后完成的,因此在正确的线程中。

至少可以说,这似乎很有趣。在c#标准中有提到这一点的地方吗?或者这是因为需要找到正确的转换而隐含的?

这是一个测试程序。第一,直接方式。第二,分两步,这更好地说明了发生了什么。为了获得更多的乐趣,我在创建委托后修改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);
            }
        }
    }
}

使用c#方法组执行代码

您正在创建一个封闭委托,它将this对象存储在委托中。(作为隐藏的第一个参数传递给方法。)

因此,当您从方法组创建委托时,将立即访问该对象以存储在委托中。

相反,当创建lambda表达式时,只有在调用委托时才能访问拥有该委托的对象。
你的lambda表达式创建了一个打开委托,它直接在委托中访问static属性。

如果它访问了非静态属性或局部变量,它将从闭包创建一个封闭委托,并且它仍然可以工作。

属性将被急切访问的事实并不是method-group成员所特有的;这是成员表达式的一般特征。

实际上是lambda创建了这个特殊情况:它的主体(以及属性访问)将被延迟,直到委托实际执行。

从规格:

7.6.4成员访问

[…成员访问可以是形式E或形式E,其中E是主表达式。

[…如果E是一个属性或索引器访问,则该值获得属性或索引器访问权限(§7.1.1),并且E是