6

我试图从一个单独的线程中使用 this.Invoke() 来访问我的表单上的控件。我正在调用一个委托,该委托指向一个以字符串 [] 作为参数的方法。

关于我的代表声明的几行:

public delegate void delVoidStringArray(string[] s);
public delVoidStringArray _dLoadUserSelect = null;
_dLoadUserSelect = LoadUsers;

从单独的线程调用委托:

Invoke(_dLoadUserSelect, sUsernames);

和调用的方法与窗体上的控件一起工作

private void LoadUsers(string[] users)
{
   //Load the list of users into a ListBox
   lstUsers.Items.AddRange(users);

   //Load the state of a CheckBox on the form
   chkUserAlways.Checked = Properties.Settings.Default.PreferDefaultUser;
}

这通常适用于具有各种参数(字符串、控件、表单和无参数)的其他代表,但每当我调用此 Invoke() 行时,我都会收到错误消息:“参数计数不匹配”。

我认为正在发生的事情是我的字符串数组被装箱到一个对象数组中,并且委托试图将这些字符串作为单独的参数传递给该方法。因此,如果字符串数组有“Bob”、“Sally”和“Joe”,它会尝试将 LoadUsers 调用为

LoadUsers("Bob", "Sally", "Joe");

这显然与签名不匹配。

这听起来像是可能发生的事情吗?我该如何解决这个问题?

4

2 回答 2

6

假设sUsernames是,string[]那么是的,你需要用

Invoke(_dLoadUserSelect, new object[] { sUsernames });

.Net 数组是协变的,所以这个赋值是有效的:

string[] sUsernames = new[] { "a", "b", "c" };
object[] objs = sUsernames;

并且在使用 params 参数调用方法时,直接传递数组,而不是作为参数数组中的第一个元素传递。您需要手动创建参数数组Invoke以获得您期望的行为。

于 2013-05-23T23:17:23.837 回答
0

以下更改将解决问题(该方法需要驻留在Form类中):

internal void LoadUsers(params string[] users)
{
    System.Action act = () =>
    {
        //Load the list of users into a ListBox
        lstUsers.Items.AddRange(users);

        //Load the state of a CheckBox on the form
        chkUserAlways.Checked = Properties.Settings.Default.PreferDefaultUser;
    });
    this.Invoke(act);
}

如果从表单外部调用,则该方法LoadUsers需要至少为internal,而不是private

因为我已经将它封装在一个 Actionact中,所以我现在可以通过this.Invoke(act);. 现在您可以LoadUsers在长时间运行的线程或任务上下文中安全地调用,例如

private void ShowUsers_Click(object sender, EventArgs e)
{
    Task.Run(() =>
    { // long running task (e.g. database query running 20 seconds)
      Thread.Sleep(20000); // wait 20 seconds
      // populate the user's list
      string[] sUsernames = new[] { "Bob", "Sally", "Joe" };
      LoadUsers(sUsernames);
      // or, passed as params: LoadUsers("Bob", "Sally", "Joe");
    });
}

在此示例中,立即将 click 事件中的操作作为任务运行可防止表单冻结并显示“未响应...”,因为该事件只是启动任务并立即退出,而任务继续单独运行。


注意:

  • Actionact在技术上在这里用作委托,但使用 Lamba 语法更容易声明(和理解)。它为您节省了一些实现工作,因为您不需要先声明委托类型然后再使用它。
  • usingparams是可选的,但LoadUsers如果直接传递参数,它会简化调用。如果你愿意,你仍然可以传递一个数组。
  • 如果您只需要更新一个控件,只说lstUsers,您也可以在控件上调用它,例如lstUsers.Invoke(act);. 在这里这是不可能的,但值得一提。
于 2020-10-16T08:36:00.373 回答