【问题标题】:C# Invoke() delegate with string array as an argument (winforms)使用字符串数组作为参数的 C# Invoke() 委托 (winforms)
【发布时间】:2013-05-23 23:11:24
【问题描述】:

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

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

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");

显然与签名不符。

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

【问题讨论】:

  • Invoke() 的第二个参数上的 params 关键字使其模棱两可。它无法判断您是使用单个数组参数还是多个字符串调用它。它假设有多个字符串。

标签: c# arrays winforms delegates


【解决方案1】:

假设sUsernamesstring[] 那么是的,你需要调用它

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

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

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

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

【讨论】:

    【解决方案2】:

    以下更改将解决问题(该方法需要驻留在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

    因为我已经将它封装在一个动作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 事件中的操作作为任务运行可防止表单冻结并显示“未响应...”,因为该事件只是启动任务并立即退出,而任务继续单独运行.


    注意:

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-05-29
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-07-30
      相关资源
      最近更新 更多