为什么必须以普通的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,您将不得不编写独立的类,如Lambda
, Lambda<S, T>
, Lambda<S, T, U>
等
这个优点我看到了原来的做法:
-
less一个types规范(只需要指定forms参数)。
-
这可以让你自由地将它用于任何
Func<int, T>
,而不仅仅是当T
是say,string
,如例子所示。 -
立即支持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式。
-
为其他代表(和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 });