如何在匿名方法中产生返回值?
基本上我有一个用于我的 BackgroundWorker
的匿名方法:
worker.DoWork += ( sender, e ) =>
{
foreach ( var effect in GlobalGraph.Effects )
{
// Returns EffectResult
yield return image.Apply (effect);
}
};
当我这样做时,编译器告诉我:
“不能使用yield语句 在匿名方法或 lambda 中 表达式”
那么在这种情况下,最优雅的方法是什么?顺便说一句,这个 DoWork 方法位于静态方法内部,以防这对解决方案很重要
Basically I have an anonymous method that I use for my BackgroundWorker
:
worker.DoWork += ( sender, e ) =>
{
foreach ( var effect in GlobalGraph.Effects )
{
// Returns EffectResult
yield return image.Apply (effect);
}
};
When I do this the compiler tells me:
"The yield statement cannot be used
inside an anonymous method or lambda
expression"
So in this case, what's the most elegant way to do this? Btw this DoWork method is inside a static method, in case that matters for the solution.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(8)
不幸的是你不能。
编译器不允许您组合这两个“神奇”的代码片段。两者都涉及重写代码以支持您想要执行的操作:
您可以但是,重写代码以返回集合,因此在您的特定情况下,我会这样做:
尽管事件
(sender, e)
返回任何内容看起来很奇怪。您确定您正在向我们展示真实的场景吗?编辑好的,我认为我明白你想在这里做什么。
您有一个静态方法调用,然后您希望在后台执行代码,然后在后台调用完成后从该静态方法返回数据。
虽然可能,但这不是一个好的解决方案,因为您实际上是暂停一个线程以等待另一个线程,而另一个线程是在您暂停线程之前直接启动的。换句话说,您所做的只是增加上下文切换的开销。
相反,您需要启动后台工作,然后当该工作完成时,处理结果数据。
Unfortunately you can't.
The compiler does not allow you to combine the two "magic" pieces of code. Both involve rewriting your code to support what you want to do:
You can, however, rewrite the code to return the collection, so in your particular case I would do this:
though it looks odd for an event
(sender, e)
to return anything at all. Are you sure you're showing a real scenario for us?Edit Ok, I think I see what you're trying to do here.
You have a static method call, and then you want to execute code in the background, and then return data from that static method once the background call completes.
This is, while possible, not a good solution since you're effectively pausing one thread to wait for another, that was started directly before you paused the thread. In other words, all you're doing is adding overhead of context switching.
Instead you need to just kick off the background work, and then when that work is completed, process the resulting data.
也许只是返回 linq 表达式并像 Yield 一样推迟执行:
Perhaps just return the linq expression and defer execution like yield:
除非我遗漏了什么,否则你不能做你所要求的事情。
(我确实有一个答案给你,所以请阅读我对为什么你不能先做你正在做的事情的解释,然后继续阅读。)
你的完整方法看起来像这样:
如果我们假设你的代码是“合法”,那么当调用
GetSomeValues
时,即使将DoWork
处理程序添加到worker
中,lambda 表达式也不会执行,直到DoWork
事件被触发。因此,对GetSomeValues
的调用完成时不会返回任何结果,并且 lamdba 可能会也可能不会在稍后阶段被调用 - 这对于GetSomeValues
方法的调用者来说为时已晚反正。最好的答案是使用
Rx 彻底改变了推送值。
IEnumerable
。 Rx 不是从可枚举请求值,而是从 IObservable由于您正在使用后台工作人员并响应事件,因此您实际上已经将值推送给您了。有了 Rx,您就可以轻松地做您想做的事情。
你有几个选择。也许最简单的方法就是这样做:
现在
GetSomeValues
方法的调用者会这样做:如果您知道
DoWork
只会触发一次,那么这种方法可能是一种好一点:这段代码看起来有点复杂,但它只是将单个 do work 事件转换为
EffectResult
对象流。那么调用代码如下所示:
Rx 甚至可以用来代替后台工作者。这可能是您的最佳选择:
调用代码与前面的示例相同。
Scheduler.ThreadPool
告诉 Rx 如何“调度”观察者订阅的处理。我希望这有帮助。
Unless I'm missing something, you can't do what you're asking.
(I do have an answer for you, so please read past my explanation of why you can't do what you're doing first, and then read on.)
You full method would look something like this:
If we assume that your code was "legal" then when
GetSomeValues
is called, even though theDoWork
handler is added toworker
, the lambda expression isn't executed until theDoWork
event is fired. So the call toGetSomeValues
completes without returning any results and the lamdba may or may not get called at a later stage - which is then too late for the caller of theGetSomeValues
method anyway.Your best answer is to the use Rx.
Rx turns
IEnumerable<T>
on its head. Instead of requesting values from an enumerable, Rx has values pushed to you from anIObservable<T>
.Since you're using a background worker and responding to an event you are effectively having the values pushed to you already. With Rx it becomes easy to do what you're trying to do.
You have a couple of options. Probably the simplest is to do this:
Now callers of your
GetSomeValues
method would do this:If you know that
DoWork
is only going to fire once, then this approach might be a little better:This code looks a little more complicated, but it just turns a single do work event into a stream of
EffectResult
objects.Then the calling code looks like this:
Rx can even be used to replace the background worker. This might be the best option for you:
The calling code is the same as the previous example. The
Scheduler.ThreadPool
tells Rx how to "schedule" the processing of subscriptions to the observer.I hope this helps.
对于新读者:在 C#5 中实现“匿名迭代器”(即嵌套在其他方法中)的最优雅的方式可能类似于 async/await 的这个很酷的技巧(不要被这些关键字混淆,下面的代码是绝对同步计算的 -请参阅链接页面中的详细信息):
C#7(现已在 Visual Studio 15 预览版中提供)支持 本地函数,允许
yield return
:For new readers: the most elegant way to implement 'anonymous iterators' (i. e. nested in other methods) in C#5 is probably something like this cool trick with async/await (don't be confused by these keywords, the code below is computed absolutely synchronously - see details in the linked page):
C#7 (available now in Visual Studio 15 Preview) supports local functions, which allow
yield return
:DoWork
是DoWorkEventHandler
类型,不返回任何内容 (void
),所以在你的情况下根本不可能。
DoWork
is of typeDoWorkEventHandler
which returns nothing (void
),so it's not possible at all in your case.
工作人员应设置 DoWorkEventArgs 的 Result 属性。
The worker should set the Result property of DoWorkEventArgs.
好吧,我做了类似的事情,它做了我想要的事情(省略了一些变量):
然后在调用站点中:
Ok so I did something like this which does what I wanted (some variables omitted):
and then in the call site:
我想用
ForEachMonad
的实现来补充 user1414213562 的答案。运行
ForEachMonad.Demo()
会产生以下输出:I wanted to supplement user1414213562's answer with an implementation of the
ForEachMonad
.Running
ForEachMonad.Demo()
produces the following output: