我是否可以有条件地使用 LINQ 创建 IEnumerable

本文关键字:LINQ 创建 IEnumerable 是否 有条件 | 更新日期: 2023-09-27 17:56:51

我必须遵循以下代码:

List<Obj> coll = new List<Obj>();
if (cond1) coll.Add(new Obj { /*...*/ });
if (cond2) coll.Add(new Obj { /*...*/ });
if (cond3) coll.Add(new Obj { /*...*/ });

有没有办法为此使用 LINQ 或集合初始值设定项?

编辑:

我想在这里使用集合初始值设定项的原因是因为我有一个对象树,我使用初始化器和 LINQ 完全初始化它。这个地方是唯一一个不遵循这个原则的地方。

var myobj = new MyBigObj 
{
    Prop1 = from .. select ..,
    Prop2 = from .. select ..,
    ...
    Prop3 = new MySmallerObj 
    {
      PropSmall1 = from .. select ..,
      PropSmall2 = from .. select ..,
      ...
    }
};

现在这根本不适合我的计划:

List<Obj> coll = new List<Obj>();
if (cond1) coll.Add(new Obj { /*...*/ });
if (cond2) coll.Add(new Obj { /*...*/ });
if (cond3) coll.Add(new Obj { /*...*/ });
myobj.Prop4 = coll;

当然,我可以将此代码放在一个单独的函数中,该函数返回IEnumerable并调用它.. :)

编辑2:

看起来我必须编写一些扩展方法,我会称之为:

new Obj[0]
.ConditionalConcat(cond1, x=>new Obj { /*...*/ })
.ConditionalConcat(cond2, x=>new Obj { /*...*/ })
.ConditionalConcat(cond3, x=>new Obj { /*...*/ })

我是否可以有条件地使用 LINQ 创建 IEnumerable

一个相当可怕的选择:

var conditions = new[] { cond1, cond2, cond3 };
var values = new[] { new Obj {...}, // First value
                     new Obj {...}, // Second value
                     new Obj { ...} // Third value
                   };
var list = conditions.Zip(values, (condition, value) => new { condition, value })
                     .Where(pair => pair.condition)
                     .Select(pair => pair.value)
                     .ToList();

不过,它并不完全比原始代码简单;)(而且它无条件地创建所有值 - 它只是有条件地将它们包含在集合中。

编辑:一种仅在需要时才构造值的替代方案:

var conditions = new[] { cond1, cond2, cond3 };
var valueProviders = new Func<Obj>[] { 
    () => new Obj {...}, // First value
    () => new Obj {...}, // Second value
    () => new Obj { ...} // Third value
};

var list = conditions.Zip(valueProviders,
                          (condition, provider) => new { condition, provider })
                     .Where(pair => pair.condition)
                     .Select(pair => pair.provider())
                     .ToList();

编辑:鉴于您要求的语法,这是一个相当简单的选择:

new List<Obj>()
    .ConditionalConcat(cond1, x=>new Obj { /*...*/ })
    .ConditionalConcat(cond2, x=>new Obj { /*...*/ })
    .ConditionalConcat(cond3, x=>new Obj { /*...*/ })

使用扩展方法:

public static List<T> ConditionalConcat<T>(this List<T> source,
                                           bool condition,
                                           Func<T> provider)
{
    if (condition)
    {
        source.Add(provider);
    }
    return source;
}

如果您的条件依赖于单个状态对象(或可以简化为的状态对象),您可以使用收益创建方法,如下所示:

IEnumerable<Obj> GetElemets(MyStatus currentStatus)
{
    if(currentStatus.Prop1 == "Foo")
       yield return new Obj {...};
    if(currentStatus.IsSomething())
       yield return new Obj {...};
    if(currentStatus.Items.Any())
       yield return new Obj {...};
    // etc...
    yield break;
}

这样,您将IEnumerable<Obj>生成逻辑与使用者逻辑分开。

老问题,但这是使用三元运算符的另一种方法? : , .Concat()Enumerable.Empty<T>()

var range1 = Enumerable.Range(1,10);
var range2 = Enumerable.Range(100,10);
var range3 = Enumerable.Range(1000,10);
var flag1 = true;
var flag2 = false;
var flag3 = true;
var sumOfCollections = (flag1 ? range1 : Enumerable.Empty<int>())
.Concat(flag2 ? range2 : Enumerable.Empty<int>())
.Concat(flag3 ? range3 : Enumerable.Empty<int>());

虽然是一个老问题,但我可以选择以某种清晰的方式解决它,而无需扩展或任何其他方法。

假设要创建的对象的条件和初始集合大小相同,我使用了索引 Where 重载方法,因此它不是有条件地添加对象,而是过滤它们,如果使用 funcs/lambdas,我们也会得到懒惰,如果我们愿意的话。

对象的实际创建无关紧要,所以我只放置了整数的装箱(您可以用真正的创建替换它,即使用 index 从另一个集合中获取它们),列表操作是为了取回整数 - 但值集合已经有 2 个元素,所以所有这些都可以扔掉(也许除了在使用懒惰的情况下使用 func 调用进行选择)。这是在 MSVS 中运行示例测试的所有代码

using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.VisualStudio.TestTools.UnitTesting;
namespace UnitTests
{
    [TestClass]
    public class Tests
    {
        [TestMethod]
        public void Test()
        {
            var conds = new[] { true, false, true };
            var values = conds.Select((c, i) => new Func<object>(() => i)).Where((f, i) => conds[i]);
            var list = values.Select(f => f()).Cast<int>().ToList();
            Assert.AreEqual(list.Count, 2);
        }
    }
}

上。这里还有带有"获取对象"的懒惰和非懒惰的单行词

var lazy1line = new[] { true, false, true }.Select((c, i) => new Func<object>(() => (DayOfWeek)i)).Where((f, i) => conds[i]).Select(f => f());
var simple1line = new[] { true, false, true }.Select((c, i) => (DayOfWeek)i).Where((f, i) => conds[i]);
Assert.AreEqual(lazy1line.Count(), simple1line.Count());