繁体   English   中英

使用C#方法组执行代码

[英]Using C# method group executes code

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

经过进一步调查,我找到了原因:我是通过方法组调用的。 我原以为使用方法组或委托/ 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);
            }
        }
    }
}

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

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

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

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

这个财产将被热切地访问的事实对于方法组成员来说并不是特别的; 这是成员表达式的一般特征。

它实际上是创建特殊情况的lambda:它的主体(以及属性访问)将被推迟到委托实际执行之前。

从规格:

7.6.4会员访问

[...]成员访问是EI形式或EI形式,其中E是主表达式。

[...] 如果E是属性或索引器访问,则获取属性或索引器访问的值(第7.1.1节),并将E重新分类为值。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM