我可以使用LINQ有条件地创建IEnumerable吗?

时间:2011-05-19 13:19:43

标签: c# .net linq collections

我必须遵循以下代码:

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或集合初始化器?

修改

我想在这里使用集合初始值设定项的原因是因为我有一个对象树,我用initialiers和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并调用..:)

EDIT2:

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

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

4 个答案:

答案 0 :(得分:5)

一个相当可怕的选择:

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;
}

答案 1 :(得分:2)

如果您的条件取决于单个状态对象(或可以减少的对象), 您可以使用yield创建一个方法,如下所示:

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>生成逻辑与消费者逻辑分开。

答案 2 :(得分:0)

旧问题,但这是使用三元运算符? :,。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>());

答案 3 :(得分:0)

尽管这是一个古老的问题,但是我可以选择以一种比较清晰的方式来解决它,而无需扩展或任何其他方法。

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

对象的实际创建是无关紧要的,因此我只放置了整数框(您可以将其替换为真实的创建,即使用索引从另一个集合中获取它们),并且列表操作用于返回整数-但值集合已经有2个元素,因此所有这些元素都可以丢弃(也许除​​了在使用懒惰的情况下通过func call选择之外)。 这是直接在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);
        }
    }
}

UPD。 这里还有带有“获取对象”的懒惰和非懒惰的一线客

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());