基本上我有一个匿名方法,我用于BackgroundWorker
:
worker.DoWork += ( sender, e ) =>
{
foreach ( var effect in GlobalGraph.Effects )
{
// Returns EffectResult
yield return image.Apply (effect);
}
};
当我这样做时,编译器告诉我:
“不能使用yield语句 在匿名方法或lambda中 表述“
所以在这种情况下,最优雅的方法是什么?顺便说一下,这个DoWork方法在静态方法中,以防对解决方案很重要。
答案 0 :(得分:14)
不幸的是你不能。
编译器不允许您组合两个“神奇”的代码片段。两者都涉及重写代码以支持您想要做的事情:
但是,您可以重写代码以返回集合,因此在您的特定情况下,我会这样做:
worker.DoWork += ( sender, e ) =>
{
return GlobalGraph.Effects
.Select(effect => image.Apply(effect));
};
虽然事件(sender, e)
看起来很奇怪,但它根本不会返回任何内容。你确定你为我们展示了一个真实的场景吗?
修改好的,我想想我看到你在这里想做什么。
您有一个静态方法调用,然后您想在后台执行代码,然后在后台调用完成后从该静态方法返回数据。
这虽然可能,但不是一个好的解决方案,因为你有效地暂停一个线程等待另一个线程,这是在你暂停线程之前直接启动的。换句话说,你所做的只是增加上下文切换的开销。
相反,您需要启动后台工作,然后在完成该工作后,处理结果数据。
答案 1 :(得分:10)
也许只返回linq表达式并延迟执行,如yield:
return GlobalGraph.Effects.Select(x => image.Apply(x));
答案 2 :(得分:5)
除非我遗漏了某些东西,否则你无法做出你所要求的事情。
(我确实有一个答案,所以请仔细阅读我的解释,说明为什么你不能做你先做的事情,然后继续阅读。)
你的完整方法看起来像这样:
public static IEnumerable<EffectResult> GetSomeValues()
{
// code to set up worker etc
worker.DoWork += ( sender, e ) =>
{
foreach ( var effect in GlobalGraph.Effects )
{
// Returns EffectResult
yield return image.Apply (effect);
}
};
}
如果我们假设您的代码是&#34;合法&#34;然后在调用GetSomeValues
时,即使将DoWork
处理程序添加到worker
,也不会执行lambda表达式,直到DoWork
事件被触发为止。所以对GetSomeValues
的调用完成后没有返回任何结果,lamdba可能会或者可能不会在稍后阶段调用 - 这对于GetSomeValues
方法的调用者来说太迟了。
您最好的答案是使用Rx。
Rx转向IEnumerable<T>
。 Rx不是从可枚举中请求值,而是从IObservable<T>
推送给您的值。
由于您正在使用后台工作程序并响应某个事件,因此您实际上已经将值推送给了您。使用Rx,您可以轻松完成您尝试做的事情。
你有几个选择。可能最简单的方法是这样做:
public static IObservable<IEnumerable<EffectResult>> GetSomeValues()
{
// code to set up worker etc
return from e in Observable.FromEvent<DoWorkEventArgs>(worker, "DoWork")
select (
from effect in GlobalGraph.Effects
select image.Apply(effect)
);
}
现在GetSomeValues
方法的来电者会这样做:
GetSomeValues().Subscribe(ers =>
{
foreach (var er in ers)
{
// process each er
}
});
如果你知道DoWork
只会发射一次,那么这种方法可能会好一点:
public static IObservable<EffectResult> GetSomeValues()
{
// code to set up worker etc
return Observable
.FromEvent<DoWorkEventArgs>(worker, "DoWork")
.Take(1)
.Select(effect => from effect in GlobalGraph.Effects.ToObservable()
select image.Apply(effect))
.Switch();
}
这段代码看起来有点复杂,但它只是将一个do work事件转换为EffectResult
个对象的流。
然后调用代码如下所示:
GetSomeValues().Subscribe(er =>
{
// process each er
});
Rx甚至可以用来替换后台工作者。这可能是您的最佳选择:
public static IObservable<EffectResult> GetSomeValues()
{
// set up code etc
return Observable
.Start(() => from effect in GlobalGraph.Effects.ToObservable()
select image.Apply(effect), Scheduler.ThreadPool)
.Switch();
}
调用代码与前一个示例相同。 Scheduler.ThreadPool
告诉Rx如何&#34;安排&#34;处理对观察者的订阅。
我希望这会有所帮助。
答案 3 :(得分:1)
DoWork
属于DoWorkEventHandler
类型,不返回任何内容(void
),
所以在你的情况下根本不可能。
答案 4 :(得分:1)
worker应该设置DoWorkEventArgs的Result属性。
worker.DoWork += (s, e) => e.Result = GlobalGraph.Effects.Select(x => image.Apply(x));
答案 5 :(得分:1)
好的,所以我做了类似的事情,做了我想要的事情(省略了一些变量):
public static void Run ( Action<float, EffectResult> action )
{
worker.DoWork += ( sender, e ) =>
{
foreach ( var effect in GlobalGraph.Effects )
{
var result = image.Apply (effect);
action (100 * ( index / count ), result );
}
}
};
然后在通话网站中:
GlobalGraph.Run ( ( p, r ) =>
{
this.Progress = p;
this.EffectResults.Add ( r );
} );
答案 6 :(得分:0)
对于新读者:在C#5中实现'匿名迭代器'(即嵌套在其他方法中)的最优雅方式可能类似于this cool trick with async/await(不要被这些关键字混淆,下面的代码绝对同步计算 - 请参见链接页面中的详细信息:
public IEnumerable<int> Numbers()
{
return EnumeratorMonad.Build<int>(async Yield =>
{
await Yield(11);
await Yield(22);
await Yield(33);
});
}
[Microsoft.VisualStudio.TestTools.UnitTesting.TestMethod]
public void TestEnum()
{
var v = Numbers();
var e = v.GetEnumerator();
int[] expected = { 11, 22, 33 };
Numbers().Should().ContainInOrder(expected);
}
C#7(Visual Studio 15 Preview中现已推出)支持local functions, which allow yield return
:
public IEnumerable<T> Filter<T>(IEnumerable<T> source, Func<T, bool> filter)
{
if (source == null) throw new ArgumentNullException(nameof(source));
if (filter == null) throw new ArgumentNullException(nameof(filter));
return Iterator();
IEnumerable<T> Iterator()
{
foreach (var element in source)
{
if (filter(element)) { yield return element; }
}
}
}