17

有没有办法摆脱 foreach 扩展方法?“break”关键字不能将扩展方法识别为有效的中断范围。

//Doesn't compile
Enumerable.Range(0, 10).ToList().ForEach(i => { System.Windows.MessageBox.Show(i.ToString()); if (i > 2)break; });

编辑:从问题中删除“linq”


请注意,代码只是一个示例,显示 break 在扩展方法中不起作用……我真正想要的是让用户能够中止处理列表。UI 线程有一个 abort 变量,而 for 循环只是中断当用户点击取消按钮时。现在,我有一个正常的 for 循环,但我想看看是否可以使用扩展方法。

4

8 回答 8

20

List<T>将其称为Foreach 与 LINQ可能更准确。

在这两种情况下,虽然没有,但没有办法打破这个循环。主要是因为它实际上并不是一个循环。这是一个方法,它接受一个在循环内调用的委托。

不过,创建具有中断功能的 ForEach 相当简单

public delegate void ForEachAction<T>(T value, ref bool doBreak);
public static void ForEach<T>(this IEnumerable<T> enumerable, ForEachAction<T> action) {
    var doBreak = false;
    foreach (var cur in enumerable) {
        action(cur, ref doBreak);
        if (doBreak) {
            break;
        }
    }
}

然后你可以重写你的代码如下

Enumerable.Range(0,10)
    .ForEach((int i,ref bool doBreak) => {
        System.Windows.MessageBox.Show(i.ToString()); 
        if ( i > 2) {doBreak = true;}
    });
于 2010-06-29T23:42:17.350 回答
11

我建议使用TakeWhile

Enumerable.Range(0, 10).TakeWhile(i => i <= 2).ToList().ForEach(i => MessageBox.Show(i.ToString()));

或者,使用Rx

Enumerable.Range(0, 10).TakeWhile(i => i <= 2).Run(i => MessageBox.Show(i.ToString()));
于 2010-06-29T23:58:46.343 回答
3

为什么不使用Where

Enumerable.Range(0, 10).Where(i => i <= 2).ToList().ForEach(...)
于 2010-06-29T23:58:16.460 回答
1

尝试使用“return”语句而不是“break”;它是一个委托函数,而不是一个真正的循环。

尝试:

Enumerable.Range(0, 10).ToList().ForEach(i => { System.Windows.MessageBox.Show(i.ToString()); if (i > 2) return; });
于 2014-12-20T00:13:33.857 回答
0

为什么不使用foreach?...

PS:开个玩笑,正如你提到的,我理解尝试看看如何使用 Linq 来做这件事的想法(出于同样的原因在此处搜索)......现在出于生产代码中的维护/准备原因,猜测一个简单foreach的就足够了有时。

于 2013-01-25T09:30:16.113 回答
0

I have a case where I need to loop actions until a condition is false and each action can modify the value used to track the condition. I came up with this variation of .TakeWhile(). The advantage here is that each item in the list can participate in some logic, then the loop can be aborted on a condition not directly related to the item.

new List<Action> {
    Func1,
    Func2
}.TakeWhile(action => {
    action();
    return _canContinue;
});
于 2017-06-28T19:48:33.760 回答
0

对于我自己,我使用了 Linq Select + FirstOrDefault。转换列表中的“每个”,但由于我们要求第一个,它会在找到第一个匹配的之后停止转换它们。

var thingOption = list.Select(thing => AsThingOption(thing))
.FirstOrDefault(option => option.HasValue) ?? Option.None<MyThing>;

如您所见,我正在将每个事物转换为选项,并且我的方法 AsThingOption 可能会成功转换,当它成功时,我们会停止迭代列表。如果 AsThingOption 方法中的条件从未成功转换,则最终结果为 None。

希望有帮助!

于 2017-04-18T21:08:44.023 回答
0

为什么不throw new Exception("Specific message")使用或使用布尔标志?

DataTable dt = new DataTable();
bool error = false;

try
{
    dt.AsEnumerable().ToList().ForEach(dr =>
    {
        if (dr["Col1"].ToString() == "")
        {
            error = true;
            throw new Exception("Specific message");
        }
        else 
            // Do something
    });
}
catch (Exception ex)
{
    if (ex.Message == "Specific message" || error) 
        // do something
    else
        throw ex;
}
于 2017-06-20T00:38:46.337 回答