126

取方法 System.Windows.Forms.Control.Invoke(Delegate method)

为什么这会产生编译时错误:

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

当方法需要一个普通的委托时?

4

8 回答 8

126

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

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

...

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

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

如果您想通过最简单的方法轻松调用Control.InvokeAction则向 Control 添加扩展方法:

public static void Invoke(this Control control, Action action)
{
    control.Invoke((Delegate) action);
}
于 2009-01-04T20:07:02.407 回答
34

厌倦了一遍又一遍地投射 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());
    }
}
于 2009-04-03T14:04:11.780 回答
12

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

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

现在它被输入了,问题就消失了(qv Skeet 的回答),我们有了这个非常简洁的语法:

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) 作为临时 hack,您可以像这样等待:

  bool wait = true;
  ThreadPool.QueueUserWorkItem ((state) =>
  {
    //do asynch stuff        
    wait = false;
  });
  while (wait) Thread.Sleep(100);
于 2011-11-10T00:41:32.197 回答
4

彼得·沃恩。你是大男人。把你的概念更进一步,我想出了这两个函数。

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 它们,将生成的图标设置为绿色或红色,然后重新启用表单上的按钮。是的,它是后台工作人员中的“parallel.for”。是的,它有很多调用开销,但对于短列表和更紧凑的代码来说可以忽略不计。

于 2012-12-07T01:45:21.630 回答
1

我试图根据@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;
    }
}

其中类型参数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>>对于其他内置的委托和表达式类型,您将不得不编写单独的类Lambda,例如Lambda<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());
    

在我的方法中,您只需声明一次类型(对于Funcs 来说也少了一个)。


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

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,这不值得。

于 2013-12-16T10:15:43.217 回答
1

派对迟到了,但你也可以这样投

this.BeginInvoke((Action)delegate {
    // do awesome stuff
});
于 2015-07-17T07:56:35.323 回答
0
 this.Dispatcher.Invoke((Action)(() => { textBox1.Text = "Test 123"; }));
于 2014-10-03T06:23:38.747 回答
0

使用 XUnit 和Fluent Assertions可以以一种我觉得非常酷的方式使用这种内联功能。

[Fact]
public void Pass_Open_Connection_Without_Provider()
{
    Action action = () => {
        using (var c = DbProviderFactories.GetFactory("MySql.Data.MySqlClient").CreateConnection())
        {
            c.ConnectionString = "<xxx>";
            c.Open();
        }
    };

    action.Should().Throw<Exception>().WithMessage("xxx");
}

[Fact]
public void Pass_Open_Connection_Without_Provider()
{
    ((Action)(() => {
        using (var c = DbProviderFactories.GetFactory("<provider>").CreateConnection())
        {
            c.ConnectionString = "<connection>";
            c.Open();
        }
    })).Should().Throw<Exception>().WithMessage("Unable to find the requested .Net Framework Data Provider.  It may not be installed.");
}
于 2019-08-29T13:40:13.207 回答