我必须遵循以下代码:
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 { /*...*/ })
答案 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());