以字符串数组作为参数的C# Invoke()委托(winforms)

sirbozc5  于 2023-08-07  发布在  C#
关注(0)|答案(2)|浏览(168)

我正试图从单独的线程使用this.Invoke()来访问窗体上的控件。我正在调用一个委托,该委托指向一个以字符串[]作为参数的方法。
关于我的delegate声明的几行:

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;
}


这通常适用于我的其他具有各种参数(string,Control,Form和无参数)的委托,但每当我调用Invoke()行时,我都会得到一个错误:“参数计数不匹配。”
我认为发生的事情是,我的字符串数组被装箱到一个对象数组中,委托试图将这些字符串作为单独的参数传递给方法。因此,如果字符串数组有“Bob”、“Sally”和“Joe”,则它将尝试调用LoadUsers作为

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


显然和签名不符
这听起来像是可能发生的事情吗?我该如何解决这个问题?

8wigbo56

8wigbo561#

假设sUsernames是一个string[],那么是的,您需要使用

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

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

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


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

bwntbbo3

bwntbbo32#

下面的更改将实现这一点(该方法需要驻留在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
因为我已经将它封装在Action act中,所以现在可以通过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事件中的操作作为Task运行可防止窗体冻结并显示“Not responses...”,因为该事件只是踢出任务并立即退出,而任务则继续单独运行。

注:

  • 这里的Action act在技术上被用作委托,但使用Lamba语法,声明(和理解)要容易得多。而且它节省了一些实现工作,因为您不需要先声明委托类型,然后再使用它。
  • 使用params是可选的,但如果直接传递参数,则可以简化对LoadUsers的调用。如果你愿意,你仍然可以传递一个数组。
  • 如果你只需要更新一个控件,比如lstUsers,你也可以在像lstUsers.Invoke(act);这样的控件上调用它。在这里这是不可能的,但值得一提。

相关问题