【问题标题】:Anonymous method in Invoke callInvoke 调用中的匿名方法
【发布时间】:2010-09-20 04:03:32
【问题描述】:

我们想在 Control.Invoke 中匿名调用委托的语法有点问题。

我们尝试了许多不同的方法,但都无济于事。

例如:

myControl.Invoke(delegate() { MyMethod(this, new MyEventArgs(someParameter)); }); 

其中 someParameter 是该方法的本地参数

以上会导致编译错误:

无法将匿名方法转换为类型“System.Delegate”,因为它不是委托类型

【问题讨论】:

    标签: c# .net compiler-errors anonymous-methods


    【解决方案1】:

    因为Invoke/BeginInvoke 接受Delegate(而不是类型化委托),所以需要告诉编译器要创建什么类型的委托; MethodInvoker (2.0) 或 Action (3.5) 是常见的选择(注意它们具有相同的签名);像这样:

    control.Invoke((MethodInvoker) delegate {this.Text = "Hi";});
    

    如果需要传入参数,那么“捕获变量”就是这样:

    string message = "Hi";
    control.Invoke((MethodInvoker) delegate {this.Text = message;});
    

    (警告:如果使用捕获 async,您需要谨慎一点,但 sync 很好 - 即上面的很好)

    另一种选择是编写扩展方法:

    public static void Invoke(this Control control, Action action)
    {
        control.Invoke((Delegate)action);
    }
    

    然后:

    this.Invoke(delegate { this.Text = "hi"; });
    // or since we are using C# 3.0
    this.Invoke(() => { this.Text = "hi"; });
    

    你当然可以对BeginInvoke做同样的事情:

    public static void BeginInvoke(this Control control, Action action)
    {
        control.BeginInvoke((Delegate)action);
    }
    

    如果您不能使用 C# 3.0,您可以使用常规实例方法执行相同操作,大概在 Form 基类中。

    【讨论】:

    • 如何在这个答案中将参数传递给您的第一个解决方案?我的意思是这个解决方案: control.Invoke((MethodInvoker) delegate {this.Text = "Hi";});
    • 为什么扩展方法被调用而不需要对 Action 进行显式转换?
    • 因为编译器可以从用法中推断出来。
    • 这与能够做到Form.Load += Loader()而不是旧的Form.Load += new EventHandler(Loader())是一样的
    【解决方案2】:

    其实你不需要使用delegate关键字。只需将 lambda 作为参数传递:

    control.Invoke((MethodInvoker)(() => {this.Text = "Hi"; }));
    

    【讨论】:

      【解决方案3】:
      myControl.Invoke(new MethodInvoker(delegate() {...}))
      

      【讨论】:

        【解决方案4】:

        您需要创建一个委托类型。匿名方法创建中的关键字“委托”有点误导。您不是在创建匿名委托,而是创建匿名方法。您创建的方法可以在委托中使用。像这样:

        myControl.Invoke(new MethodInvoker(delegate() { (MyMethod(this, new MyEventArgs(someParameter)); }));
        

        【讨论】:

          【解决方案5】:

          为了完整起见,这也可以通过Action方法/匿名方法组合来完成:

          //Process is a method, invoked as a method group
          Dispatcher.Current.BeginInvoke((Action) Process);
          //or use an anonymous method
          Dispatcher.Current.BeginInvoke((Action)delegate => {
            SomeFunc();
            SomeOtherFunc();
          });
          

          【讨论】:

          • Invoke((Action) Process); 是最好的答案,谢谢!
          【解决方案6】:

          我对其他建议有疑问,因为我有时想从我的方法中返回值。如果您尝试使用带有返回值的 MethodInvoker,它似乎不喜欢它。所以我使用的解决方案是这样的(很高兴听到一种让这个更简洁的方法——我使用的是 c#.net 2.0):

              // Create delegates for the different return types needed.
              private delegate void VoidDelegate();
              private delegate Boolean ReturnBooleanDelegate();
              private delegate Hashtable ReturnHashtableDelegate();
          
              // Now use the delegates and the delegate() keyword to create 
              // an anonymous method as required
          
              // Here a case where there's no value returned:
              public void SetTitle(string title)
              {
                  myWindow.Invoke(new VoidDelegate(delegate()
                  {
                      myWindow.Text = title;
                  }));
              }
          
              // Here's an example of a value being returned
              public Hashtable CurrentlyLoadedDocs()
              {
                  return (Hashtable)myWindow.Invoke(new ReturnHashtableDelegate(delegate()
                  {
                      return myWindow.CurrentlyLoadedDocs;
                  }));
              }
          

          【讨论】:

            【解决方案7】:

            我喜欢用 Action 代替 MethodInvoker,它更短,看起来更干净。

            Invoke((Action)(() => {
                DoSomething();
            }));
            
            // OR
            
            Invoke((Action)delegate {
                DoSomething();
            });
            

            例如。

            // Thread-safe update on a form control
            public void DisplayResult(string text){
                if (txtResult.InvokeRequired){
                    txtResult.Invoke((Action)delegate {
                        DisplayResult(text);
                    });
                    return;
                }
            
                txtResult.Text += text + "\r\n";
            }
            

            【讨论】:

              【解决方案8】:

              我一直不明白为什么这会对编译器产生影响,但这已经足够了。

              public static class ControlExtensions
              {
                  public static void Invoke(this Control control, Action action)
                  {
                      control.Invoke(action);
                  }
              }
              

              奖励:添加一些错误处理,因为如果您在后台线程中使用Control.Invoke,您可能正在更新控件的文本/进度/启用状态,而不关心控件是否已经处置。

              public static class ControlExtensions
              {
                  public static void Invoke(this Control control, Action action)
                  {
                      try
                      {
                          if (!control.IsDisposed) control.Invoke(action);
                      }
                      catch (ObjectDisposedException) { }
                  }
              }
              

              【讨论】:

                猜你喜欢
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 2016-08-05
                • 1970-01-01
                • 2015-11-06
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                相关资源
                最近更新 更多