为什么必须以普通的Delegate参数的forms提供lambdaexpression式

采取方法System.Windows.Forms.Control.Invoke(委托方法)

为什么这给编译时间错误:

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

当方法期望一个普通的代表?

lambdaexpression式可以转换为委托types或expression式树 – 但它必须知道哪个委托types。 只知道签名是不够的。 例如,假设我有:

 public delegate void Action1(); public delegate void Action2(); ... Delegate x = () => Console.WriteLine("hi"); 

你会期望x引用的对象的具体types是什么? 是的,编译器可以用适当的签名生成一个新的委托types,但是这样做很less用,而且错误检查的机会很less。

如果要使用Action简单地调用Control.Invoke ,最简单的方法是将扩展方法添加到Control:

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

厌倦了一遍又一遍的重复铸造羊肚菌?

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

现在它已经被input了,这个问题就消失了(qke Skeet's awer),我们有这个非常简洁的语法:

 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 (QMSMS的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); 

彼得·沃恩 你是达人。 把你的概念做得更远一点,我想出了这两个function。

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

也许有点懒,但我不必设置工人完成的function,在这种情况下,这是超级方便的

 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他们,设置结果图标为绿色或红色,并重新启用窗体上的button。 是的,这是一个背景工作者的“平行”。 是的,它是一个调用开销很多,但它可以忽略短名单,更紧凑的代码。

我试图build立这个@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; } } 

其中types参数S是forms参数(input参数,这是推断其余types所需的最小值)。 现在你可以这样称呼它:

 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>>您可以在同一个类中有类似的额外重载。 对于其他内置的委托和expression式types,您将不得不编写独立的类,如LambdaLambda<S, T>Lambda<S, T, U>

这个优点我看到了原来的做法:

  1. less一个types规范(只需要指定forms参数)。

  2. 这可以让你自由地将它用于任何Func<int, T> ,而不仅仅是当T是say, string ,如例子所示。

  3. 立即支持expression式。 在之前的方法中,您将不得不再次指定types,如:

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

    expression式。

  4. 为其他代表(和expression式)types扩展类同样如此繁琐。

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

在我的方法中,你只需要声明一次types(对于Func来说也less一次)。


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

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

所以事情减less到:

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

这甚至更less打字,但是你失去了某种types的安全,而且这是不值得的。

  this.Dispatcher.Invoke((Action)(() => { textBox1.Text = "Test 123"; })); 

晚会晚了,但你也可以投这样的

 this.BeginInvoke((Action)delegate { // do awesome stuff });