C#Func Delegate抛出线程异常

时间:2009-07-31 14:50:00

标签: c#

所以,也许我误解了Func的用法但是

Func<ComboBox, string> getCurrentValue = s => s.SelectedValue.ToString();

从我的Workerthread调用getCurrentValue(cb_message_type)时创建一个线程错误。 什么是获得Combobox选定值的最佳解决方案?

非常感谢,
rAyt

修改
MSDN

“lambda表达式的基础类型是通用Func委托之一。这使得可以将lambda表达式作为参数传递,而无需将其明确地分配给委托。”

5 个答案:

答案 0 :(得分:5)

由于Windows控件具有线程关联性,因此您有两个选项:

  1. 在执行线程代码之前查询此数据,例如将其作为状态传递给worker
  2. 通过Control.Invoke
  3. 在worker中查询它

    由于第一个是微不足道的,我将使用捕获的变量给出第二个示例:

    object value = null;
    yourCombo.Invoke((MethodInvoker) delegate {value=yourCombo.SelectedValue;});
    string s = value.ToString();
    

    这里delegate {...}内的位发生在UI线程上,即使它周围的代码在工作线程上。您可以将上面的内部混合到您的func中,或者在切换线程后调用整个函数。

答案 1 :(得分:2)

您需要使用该委托调用Control.Invoke - 或让委托自己调用它。 使用lambda表达式不会改变Windows窗体的线程要求 - 它只是使创建委托更容易。

您可能想要一个方便的方法来执行此操作:

// (Untested)
public static Func<TControl, TResult> WrapInvocation(Func<TControl,TResult> func)
    where TControl : Control
{
    return control => {
        return (TResult) control.Invoke(func);
    };
}

用作:

Func<ComboBox, string> getCurrentValue = s => s.SelectedValue.ToString();
getCurrentValue = WrapInvocation(getCurrentValue);

然后你可以从任何线程中调用getCurrentValue(comboBox)

答案 2 :(得分:1)

问题是UI控件只能在UI线程上使用

您需要在另一个线程中调用Invoke方法,如下所示:

Func<ComboBox, string> getCurrentValue =
    s => s.Invoke(new Func<object>(() => s.SelectedValue)).ToString();

Invoke方法接受一个委托并在UI线程上执行它。

答案 3 :(得分:1)

一般来说,您无法从创建它们之外的线程访问UI控件。要解决这个问题,您必须检查有问题的控件上的ISynchronizeInvoke.InvokeRequired和分支,调用委托等,或使用SynchronizationContext。第一种选择非常麻烦,而第二种选择非常优雅:

var synchronizationContext = WindowsFormsSynchronizationContext.Current;
string currentValue = "";    

synchronizationContext.Send(
    () => currentValue = getCurrentValue(comboBox), 
    null);

答案 4 :(得分:0)

如果线程只是简单地读取ComboBox,那么最好的选择(如果可行的话)可能是在线程上有一个事件处理程序,它在任何时候更改时都会获取ComboBox值,然后通过可以从中读取的属性公开该值任何线程。