采用System.Windows.Forms.Control.Invoke(Delegate方法)方法

为什么会出现编译时错误:

string str = "woop";
Invoke(() => this.Text = str);
// Error: Cannot convert lambda expression to type 'System.Delegate'
// because it is not a delegate type

然而这很好用:

string str = "woop";
Invoke((Action)(() => this.Text = str));

当方法需要普通的Delegate?

有帮助吗?

解决方案

lambda表达式可以转换为委托类型或表达式树 - 但它必须知道哪个委托类型。只知道签名是不够的。例如,假设我有:

public delegate void Action1();
public delegate void Action2();

...

Delegate x = () => Console.WriteLine("hi");

您希望x引用的对象的具体类型是什么?是的,编译器可以生成一个带有适当签名的新委托类型,但这很少有用,最终你可以减少错误检查的机会。

如果您希望通过Control.Invoke轻松调用Action,最简单的方法是向控件添加扩展方法:

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

其他提示

厌倦了一遍又一遍地抛弃lambda?

public sealed class Lambda<T>
{
    public static Func<T, T> Cast = x => x;
}

public class Example
{
    public void Run()
    {
        // Declare
        var c = Lambda<Func<int, string>>.Cast;
        // Use
        var f1 = c(x => x.ToString());
        var f2 = c(x => "Hello!");
        var f3 = c(x => (x + x).ToString());
    }
}

人们得到这个的时间只有十分之九,因为他们正试图编组到UI线程上。这是懒惰的方式:

static void UI(Action action) 
{ 
  System.Windows.Threading.Dispatcher.CurrentDispatcher.BeginInvoke(action); 
}

现在已经输入了,问题就消失了(qv Skeet's anwer),我们有这种非常简洁的语法:

int foo = 5;
public void SomeMethod()
{
  var bar = "a string";
  UI(() =>
  {
    //lifting is marvellous, anything in scope where the lambda
    //expression is defined is available to the asynch code
    someTextBlock.Text = string.Format("{0} = {1}", foo, bar);        
  });
}

对于奖励积分,这是另一个提示。您不会为UI内容执行此操作,但在需要SomeMethod阻塞直到完成(例如请求/响应I / O,等待响应)的情况下,请使用 WaitHandle (qv msdn WaitAll,WaitAny,WaitOne)。

请注意,AutoResetEvent是WaitHandle的衍生产品。

public void BlockingMethod()
{
  AutoResetEvent are = new AutoResetEvent(false);
  ThreadPool.QueueUserWorkItem ((state) =>
  {
    //do asynch stuff        
    are.Set();
  });      
  are.WaitOne(); //don't exit till asynch stuff finishes
}

最后的提示是因为事情可能会纠结:WaitHandles会阻止线程。这是他们应该做的。如果您尝试在停止的情况下编组到UI线程,则您的应用程序将挂起。在这种情况下(a)一些严重的重构是有序的,(b)作为临时黑客,你可以这样等待:

  bool wait = true;
  ThreadPool.QueueUserWorkItem ((state) =>
  {
    //do asynch stuff        
    wait = false;
  });
  while (wait) Thread.Sleep(100);
彼得沃恩。你是个男人。 进一步考虑你的概念,我提出了这两个功能。

private void UIA(Action action) {this.Invoke(action);}
private T UIF<T>(Func<T> func) {return (T)this.Invoke(func);}

我将这两个功能放入我的表格应用程序中,我可以像这样的后台工作人员打电话

int row = 5;
string ip = UIF<string>(() => this.GetIp(row));
bool r = GoPingIt(ip);
UIA(() => this.SetPing(i, r));

也许有点懒,但我没有设置工人完成的功能, 在这样的情况下,这是非常方便的

private void Ping_DoWork(object sender, System.ComponentModel.DoWorkEventArgs e)
{
  int count = this.dg.Rows.Count;
  System.Threading.Tasks.Parallel.For(0, count, i => 
  {
    string ip = UIF<string>(() => this.GetIp(i));
    bool r = GoPingIt(ip);
    UIA(() => this.SetPing(i, r));
  });
  UIA(() => SetAllControlsEnabled(true));
}

基本上,从gui DataGridView获取一些ip地址,ping它们,将结果图标设置为绿色或红色,并重新启用表单上的按钮。是的,它是<!> quot; parallel.for <!> quot;在背景工作者。是的,它有很多调用开销,但它对于短列表来说可以忽略不计,而且代码更紧凑。

我尝试在 @Andrey Naumov 的回答中构建它。可能这是一个小小的改进。

public sealed class Lambda<S>
{
    public static Func<S, T> CreateFunc<T>(Func<S, T> func)
    {
        return func;
    }

    public static Expression<Func<S, T>> CreateExpression<T>(Expression<Func<S, T>> expression)
    {
        return expression;
    }

    public Func<S, T> Func<T>(Func<S, T> func)
    {
        return func;
    }

    public Expression<Func<S, T>> Expression<T>(Expression<Func<S, T>> expression)
    {
        return expression;
    }
}

其中type参数S是形式参数(输入参数,这是推断其余类型所需的最小值)。现在你可以这样称呼它:

var l = new Lambda<int>();
var d1 = l.Func(x => x.ToString());
var e1 = l.Expression(x => "Hello!");
var d2 = l.Func(x => x + x);

//or if you have only one lambda, consider a static overload
var e2 = Lambda<int>.CreateExpression(x => "Hello!");

您可以在同一个类中为Action<S>Expression<Action<S>>添加额外的重载。对于内置委托和表达式类型的其他,您必须编写单独的类,如LambdaLambda<S, T>Lambda<S, T, U>等。

这方面的优点我看到了最初的方法:

  1. 少一种类型规范(只需要指定形式参数)。

  2. 这使您可以自由地使用它来对抗任何Func<int, T>,而不仅仅是T时,string,如示例所示。

  3. 立即支持表达式。在之前的方法中,您必须再次指定类型,例如:

    var e = Lambda<Expression<Func<int, string>>>.Cast(x => "Hello!");
    
    //or in case 'Cast' is an instance member on non-generic 'Lambda' class:
    var e = lambda.Cast<Expression<Func<int, string>>>(x => "Hello!");
    

    表达式。

  4. 扩展其他委托(和表达式)类型的类同样繁琐。

    var e = Lambda<Action<int>>.Cast(x => x.ToString());
    
    //or for Expression<Action<T>> if 'Cast' is an instance member on non-generic 'Lambda' class:
    var e = lambda.Cast<Expression<Action<int>>>(x => x.ToString());
    
  5. 在我的方法中,你只需要声明一次类型(对于Func s来说也少一些)。


    实现安德烈答案的另一种方式就是不完全通用

    public sealed class Lambda<T>
    {
        public static Func<Func<T, object>, Func<T, object>> Func = x => x;
        public static Func<Expression<Func<T, object>>, Expression<Func<T, object>>> Expression = x => x;
    }
    

    所以事情减少到:

    var l = Lambda<int>.Expression;
    var e1 = l(x => x.ToString());
    var e2 = l(x => "Hello!");
    var e3 = l(x => x + x);
    

    这更少打字,但你失去了某种类型的安全性, imo,这是不值得的。

派对迟到但你也可以像这样投票

this.BeginInvoke((Action)delegate {
    // do awesome stuff
});
 this.Dispatcher.Invoke((Action)(() => { textBox1.Text = "Test 123"; }));
许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top