我是否可以有条件地使用 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 { /*...*/ })
一个相当可怕的选择:
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());