为什么 lambda 表达式作为普通委托参数提供时必须进行强制转换
采用方法 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));
当方法需要一个普通的委托时?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(9)
聚会有点晚了,但你也可以像这样投射
Bit late to the party but you can also cast like this
使用 XUnit 和 Fluent Assertions 可以以我认为非常酷的方式使用此内联功能。
之前
之后
Playing with XUnit and Fluent Assertions it was possible to use this inline capability in a way I find really cool.
Before
After
其他答案在编写时是正确的,但从 C# 10.0(从 2021 年起)开始,编译器可以推断出合适的委托类型(例如某些
Func<...>< /code>、
Action<...>
或生成的委托类型)在这种情况下。请参阅 C# 10 功能 - Lambda改进。
注释假定签名
Invoke(Delegate method)
如您的问题中所示。 当然,如果该方法需要特定的(非抽象)委托类型,C# 将尝试将 lambda 转换为该类型,C# 10.0 之前的情况也是如此。Other answers were correct at the time they were written, but starting from C# 10.0 (from 2021), the compiler can infer a suitable delegate type (like some
Func<...>
,Action<...>
or generated delegate type) in such cases.See C# 10 Features - Lambda improvements.
Comments assume the signature
Invoke(Delegate method)
as in your question. Of course if the method wants a particular (not abstract) delegate type, C# will attempt to convert the lambda into that one, as was also the case before C# 10.0.lambda 表达式可以转换为委托类型或表达式树 - 但它必须知道哪种委托类型。 仅仅知道签名是不够的。 例如,假设我有:
您期望 x 引用的对象的具体类型是什么? 是的,编译器可以生成具有适当签名的新委托类型,但这很少有用,而且您最终进行错误检查的机会也更少。
如果您想轻松地使用
Action
调用Control.Invoke
,最简单的方法就是向 Control 添加扩展方法:A lambda expression can either be converted to a delegate type or an expression tree - but it has to know which delegate type. Just knowing the signature isn't enough. For instance, suppose I have:
What would you expect the concrete type of the object referred to by
x
to be? Yes, the compiler could generate a new delegate type with an appropriate signature, but that's rarely useful and you end up with less opportunity for error checking.If you want to make it easy to call
Control.Invoke
with anAction
the easiest thing to do is add an extension method to Control:厌倦了一遍又一遍地转换 lambda?
Tired of casting lambdas over and over?
十分之九的情况下,人们会遇到这种情况,因为他们正在尝试编组到 UI 线程。 这是一种懒惰的方法:
现在它已经被输入了,问题就消失了(qv Skeet 的回答),我们有这个非常简洁的语法:
对于奖励点,这里有另一个提示。 您不会对 UI 内容执行此操作,但在需要 SomeMethod 阻止直到其完成的情况下(例如请求/响应 I/O,等待响应),请使用 WaitHandle(qv msdn WaitAll、WaitAny、WaitOne)。
请注意,AutoResetEvent 是 WaitHandle 的派生物。
最后一个提示是因为事情可能会变得混乱:WaitHandles 会阻止线程。 这就是他们应该做的。 如果您在 UI 线程停止时尝试封送至 UI 线程,您的应用程序将挂起。 在这种情况下,(a)需要进行一些认真的重构,(b)作为临时黑客,您可以像这样等待:
Nine tenths of the time people get this because they are trying to marshal onto the UI thread. Here's the lazy way:
Now that it's typed, the problem goes away (qv Skeet's anwer) and we have this very succinct syntax:
For bonus points here's another tip. You wouldn't do this for UI stuff but in cases where you need SomeMethod to block till it completes (eg request/response I/O, waiting for the response) use a WaitHandle (qv msdn WaitAll, WaitAny, WaitOne).
Note that AutoResetEvent is a WaitHandle derivative.
And a final tip because things can get tangled: WaitHandles stall the thread. This is what they're supposed to do. If you try to marshal onto the UI thread while you have it stalled, your app will hang. In this case (a) some serious refactoring is in order, and (b) as a temporary hack you can wait like this:
彼得·沃恩. 你是个大男人。
进一步阐述你的概念,我想出了这两个函数。
我将这两个函数放入我的表单应用程序中,我可以像这样从后台工作人员进行调用
也许有点懒,但我不必设置工作人员完成的功能,
在这种情况下,它非常方便。
本质上,从 gui DataGridView 获取一些 IP 地址,对它们执行 ping 操作,将生成的图标设置为绿色或红色,然后重新启用表单上的按钮。 是的,它是后台工作者中的“parallel.for”。 是的,这是大量的调用开销,但对于短列表和更紧凑的代码来说,它可以忽略不计。
Peter Wone. you are da man.
Taking your concept a bit further, I came up with these two functions.
I place these two functions into my Form app, and I can make calls from background workers like this
Maybe a bit lazy, but i don't have to setup worker done functions,
which comes in super handy in cases such as this
Essentially, get some ip addresses from a gui DataGridView, ping them, set the resulting icons to green or red, and reenable buttons on the form. Yes, it is a "parallel.for" in a backgroundworker. Yes it is a LOT of invoking overhead, but its negligible for short lists, and much more compact code.
我尝试根据 @Andrey Naumov 的答案构建此内容。 也许这是一个小小的改进。
其中类型参数
S
是形式参数(输入参数,推断其余类型所需的最低参数)。 现在您可以这样调用它:您可以在同一个类中类似地为
Action
和Expression>
提供额外的重载。 对于其他内置委托和表达式类型,您必须编写单独的类,例如Lambda
、Lambda
、Lambda
等。与原始方法相比,我认为这样做的优点是:
少了一个类型规范(仅需要指定形式参数)。
这让您可以自由地将它用于任何
Func
,而不仅仅是当T
是string
时,如示例所示。立即支持表达式。 在之前的方法中,您必须再次指定类型,例如:
用于表达式。
为其他委托(和表达式)类型扩展类与上面类似很麻烦。
在我的方法中,您只需声明类型一次(对于
Func
来说也少声明一次)。实现安德烈的答案的另一种方法是不完全通用,
所以事情会减少到:
这甚至更少的打字,但你失去了一定的类型安全性,我认为,这是不值得的。
I tried to build this upon @Andrey Naumov's answer. May be this is a slight improvement.
Where type parameter
S
is the formal parameter (the input parameter, which is minimum required to infer rest of the types). Now you can call it like:You can have additional overloads for
Action<S>
andExpression<Action<S>>
similarly in the same class. For other built in delegate and expression types, you will have to write separate classes likeLambda
,Lambda<S, T>
,Lambda<S, T, U>
etc.Advantage of this I see over the original approach:
One less type specification (only the formal parameter needs to be specified).
Which gives you the freedom to use it against any
Func<int, T>
, not just whenT
is say,string
, as shown in examples.Supports expressions straight away. In the earlier approach you will have to specify types again, like:
for expressions.
Extending the class for other delegate (and expression) types is similarly cumbersome like above.
In my approach you have to declare types only once (that too one less for
Func
s).One another way to implement Andrey's answer is like not going fully generic
So things reduce to:
That's even less typing, but you lose certain type safety, and imo, this is not worth it.