我一直在阅读LINQ to Objects,现在我的同事们希望我把它呈现给他们。
现在,我对运算符和语法选择有了一个很好的理解,但我听说你可以通过LINQ 避免重度嵌套循环。我无法想出一套好的“之前和之后”代码清单来证明这一点。
我在Magennis的书中找到了使用和不使用LINQ进行排序和分组的一个很好的例子,他还有一个编写xml的例子。但那些嵌套循环呢?这是否是一个现实的主张,因为我们通常需要一个或两个foreach
循环来迭代查询结果?
如果有人能够向我解释这个想法(理想情况下是具体的例子),我将非常感激。
答案 0 :(得分:9)
假设您有很多产品,例如:
var products = new List<Product>
{
new Product { Id = 1, Category = "Electronics", Value = 15.0 },
// etc.
};
并且您想要找到价值&gt;的所有产品$ 100.0,按类别分组,您可以使用foreach
执行此操作:
var results = new Dictionary<string, List<Product>>();
foreach (var p in products)
{
if (p.value > 100.0)
{
List<Product> productsByGroup;
if (!results.TryGetValue(p.Category, out productsByGroup))
{
productsByGroup = new List<Product>();
results.Add(p.Category, productsByGroup);
}
productsByGroup.Add(p);
}
}
或者,您可以简单地使用LINQ方法:
var results = products.Where(prod => prod.Value > 100.0)
.GroupBy(prod => prod.Category);
或使用LINQ表达式语法:
var results = from p in products
where p.Value > 100.0
group p by p.Category;
更简洁,更不易出错。
答案 1 :(得分:8)
这是一种可以用Linq删除的嵌套循环。
foreach(SomeClass item in Items)
{
foreach(SomeOtherClass subItem in item.SubItems)
{
// ...
}
}
可以变成:
foreach(SomeOtherClass subItem in Items.SelectMany(i => i.SubItems))
{
}
使用the SelectMany
extension method on IEnumerable
。
这个非常有用的地方是nested loop double-break scenarios。
答案 2 :(得分:2)
var results = new List<Object>();
foreach(var i in list)
{
if (i.property == value)
{
foreach(var j in list.SubList)
{
if (j.other == something)
{
results.push(j);
}
}
}
}
可能是:
var results = list.Where(i => i == value)
.SelectMany(i => i.SubList)
.Where(j => j.other == something)
.ToList();
答案 3 :(得分:1)
这是一个有点人为的例子。
假设您获得了一个字符串列表,您的任务是找到并返回HashSet<>
中这些字符串中的所有控制字符。
var listOStrings = new List<string> { ... };
var result = new HashSet<char>();
您可能会这样做:
foreach (var str in listOStrings)
{
foreach (var c in str)
{
if (Char.IsControl(c))
{
result.Add(c);
}
}
}
或使用LINQ:
result = new HashSet<char>(
listOStrings
.SelectMany(str => str.Where(Char.IsControl)));
答案 4 :(得分:1)
最有用的示例是您可以在LINQ中使用内置方法,例如All
和Any
。像这样:
bool hasCats = listOfAnimals.Any(animal => animal.Type == "Cat");
使用带有if和break和bool检查变量的for循环写一下,我想这至少会有五行代码来做同样的事情。嗯,让我们看看:
bool hasCats = false;
foreach(Animal animal in listOfAnimals)
{
if (animal.Type == "Cat")
{
hasCats = true;
break;
}
}
ooops,9行。你需要仔细阅读至少三个以了解代码的作用。
嗯,更多相同。假设哺乳动物有一个真实的类型层次。
IEnumerable<Cat> allCats = listOfAnimals.OfType<Cat>();
这将返回所有可以转换为Cat
的动物,并将它们返回,投放并准备使用。用循环写的:
List<Cat> allCats = new List<Cat>();
foreach(var animal in listOfAnimals)
{
var cat = animal as Cat;
if (cat != null)
{
allCats.Add(cat);
}
}
老实说,你应该将其分解为一个单独的方法,并使用yield return cat;
来获得与LINQ版本相同的惰性行为。
但我更喜欢查询语法。只需极少的噪音即可阅读。
var cats =
from cat in listOfCats
where cat.Age > 5
where cat.Color == "White"
select cat;
使用普通循环编写
List<Cat> cats = new List<Cat>();
foreach(Cat cat in listOfCats)
{
if (cat.Age > 5)
{
if (cat.Color == "White")
{
cats.Add(cat);
}
}
}
再次需要使用yield return
的单独方法来获得相同的延迟评估行为。