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

发布于 2024-11-07 22:58:00 字数 1174 浏览 0 评论 0原文

我必须遵循以下代码:

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 的单独函数中并调用它..:)

EDIT2:

看来我必须编写一些扩展代码我会这样调用的方法:

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

I have to following code:

List<Obj> coll = new List<Obj>();

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

Is there a way to use LINQ or collection initializers for that?

EDIT:

The reason I want to use a collection initializer here is because I have an object tree which I do completely initialize with initialiers and LINQ. This spot is the only one which doesn't follow this principle.

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

And now this simply doesn't fit in my scheme:

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;

Sure I could put this code in a separate function that returns IEnumerable and call that.. :)

EDIT2:

It looks like I have to code some extension method which I would call like:

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

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

送你一个梦 2024-11-14 22:58:00

一个相当可怕的选择:

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

One fairly horrible option:

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

It's not exactly simpler than the original code though ;) (And also it unconditionally creates all the values - it's only conditionally including them in the collection.)

EDIT: An alternative which only constructs the values when it needs to:

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

EDIT: Given your requested syntax, this is a fairly easy option:

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

with an extension method:

public static List<T> ConditionalConcat<T>(this List<T> source,
                                           bool condition,
                                           Func<T> provider)
{
    if (condition)
    {
        source.Add(provider);
    }
    return source;
}
烟花易冷人易散 2024-11-14 22:58:00

如果您的条件取决于单个状态对象(或可以简化为的对象),
您可以使用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 生成逻辑与使用者逻辑分开。

If your conditions depend on a single status object (or something that can reduced to),
you can create a method using yield, like the following:

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

In this way, you will separate the IEnumerable<Obj> generation logic, from the consumer logic.

杀手六號 2024-11-14 22:58:00

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

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

Old question, but here is another approach using ternery operators ? :, .Concat() and 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>());
柠北森屋 2024-11-14 22:58:00

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

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

对象的实际创建并不相关,所以我只对整数进行装箱(您可以将其替换为实际创建,即使用索引从另一个集合中获取它们),列表操作用于取回整数 - 但值集合已经有 2元素,所以所有这些都可以被丢弃(也许除​​了使用 func 调用的 select 之外,以防使用懒惰)。
中运行示例测试的所有代码

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

以下是在 MSVS 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());

Though an old question, but I have an option to solve it in a somewhat clear way and without extensions or any other methods.

Assuming that conditions and initial collection of objects to be created are of same size, I used indexed Where overload approach, so it is not adding objects conditionally, but rather filtering them, with a use of funcs/lambdas we get also laziness, if we want.

The actual creation of objects is not relevant, so I put just boxing of ints (you could replace it with real creation, i.e. getting them from another collection using index), and list manipulation is for getting ints back - but values collection already has 2 elements, so all this could be thrown away (maybe except select with func call in case of using laziness).
Here is the all the code for running sample test right in 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.
Here also lazy and non-lazy one-liners with "getting object"

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());
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文