使用 Let 简化 LINQ 代码
解决的问题
有时,在我们的查询中,我们必须一遍又一遍地用相同的方式修改列值。当我们编写查询时,我们有一种复制和粘贴的冲动,但我们已经习惯了这种冲动是错误的。LINQ 为我们提供了一种模块化这些函数的方法,然后在查询的其他地方引用它们,而无需复制和粘贴。
考虑以下查询,它查询一组电子邮件中不包含“email.com”的客户,并返回每个非“email.com”域的计数:
from c in customers
where c.Email.Substring(c.Email.IndexOf("@") + 1) != "email.com"
group c by c.Email.Substring(c.Email.IndexOf("@") + 1) into g
select new { Total = g.Count(), Domain = g.Key };`
我们在两个地方使用域字符串解析逻辑:一是过滤掉不需要的域,二是将客户分组。代码在两个地方重复 - 重复的代码容易出现缺陷,最好重构为通用块。LINQ中的let语句允许我们这样做。
我们的场景
我们的任务是编写一个新查询,返回属于两个位置之一的员工集,然后按这些位置排序。生成的查询如下所示:
from e in employees
where e.GroupCode.ToUpper().Substring(0, 2) == "AA"
||
e.GroupCode.ToUpper().Substring(0, 2) == "BB"
orderby e.GroupCode.ToUpper().Substring(0, 2)
select e;
此重复条款:
e.GroupCode.ToUpper().Substring(0, 2)
``
`
- represents Location – the building where the employee is physically located. It would be nice if this were broken out separately from the other fields but, unfortunately, we must do that ourselves in code. Looking at this, we consider that it would be nice to streamline and centralize this code.
After a little research, we find that this is what the _let _keyword in LINQ is for. Let allows you to define a variable which contains the expression you want and use it as if it were any other field. We can take our query from before and streamline it to look like this:
```csharp
from e in employees
let location = e.GroupCode.ToUpper().Substring(0, 2)
where location == "AA"
||
location == "BB"
orderby location
select e;
现在,location变量包含 GroupCode 字符串操作表达式,当我们在后续行中引用它时,查询的行为就像代码在那里一样。此查询返回的结果与上一个查询相同。
链式 Let
Let 语句可以充当微型 where 语句。考虑以下动物列表:
var animals = new List<Animal>
{
new Animal {Name = "Whale", Class = "Mammal", Location = "Ocean"},
new Animal {Name = "Bear", Class = "Mammal", Location = "Forest"},
new Animal {Name = "Hawk", Class = "Bird", Location = "Forest"},
new Animal {Name = "Tuna", Class = "Fish", Location = "Ocean"}
};
假设我们想要返回以下动物列表:
- 并不可怕
- 是我们最喜欢的动物之一
- 住在森林里
以下查询将返回这些结果:
from a in animals
where !(new List<string> { "Bear", "Shark" }.Contains(a.Name))
&& new List<string> { "Hawk", "Shark" }.Contains(a.Name)
&& a.Location == "Forest"
select a;
这有点不清楚,因为上面两个列表的含义(熊和鲨鱼元素以及鹰和鲨鱼元素)是模棱两可的。开发人员在编写时可能知道哪个是他最喜欢的,哪个是可怕的(以及这两个列表之间的关系是什么),但一周后再看,很难知道。
用简单的英语来说,上述查询的意思是:
从动物列表中给我这样的动物:这个列表:“熊”,“鲨鱼”不包含名称,而这个列表:“鹰”,“鲨鱼”包含名称,并且位置是“森林”。
这里没有需要用let语句解决的重复问题,但是我们可以使用它让一切变得更加清晰:
var results = from a in animals
let favoriteAnimals = new List<string> { "Hawk", "Shark" }
let scaryAnimals = new List<string> { "Bear", "Shark" }
let isFavorite = favoriteAnimals.Contains(a.Name)
let isScary = scaryAnimals.Contains(a.Name)
let livesInTheWoods = a.Location == "Forest"
where (!isScary && isFavorite && livesInTheWoods)
select a;
在这里,我们在查询中内联定义我们的列表 favoriteAnimals 和 scaryAnimals。然后我们创建两个布尔变量,反映给定动物的名称是否在该列表中。然后我们添加一个livesInTheWoods变量来检查位置,并在 where 子句中将这三个元素连接在一起。
这两个查询的结果都是Hawk – 我最喜欢的唯一一种我不害怕的动物,它生活在森林里。这两个查询都完全正确,但第二个查询更清楚地反映了集合的含义。如果后来我决定不再害怕鲨鱼,那么很明显我需要从 scaryAnimals 列表中删除该元素。将其与上一个查询进行比较:
from a in animals
where !(new List<string> { "Bear", "Shark" }.Contains(a.Name))
&& new List<string> { "Hawk", "Shark" }.Contains(a.Name)
&& a.Location == "Forest"
select a;
如果我不再害怕鲨鱼,那我需要从哪个列表中删除它?第一个还是第二个?
重要的是要考虑到这种let方法会导致更长的查询。但更短并不总是更好,更少的代码也并不总是比更多更好。有时,如果权衡利弊,多写几行代码是值得的。
免责声明:本内容来源于第三方作者授权、网友推荐或互联网整理,旨在为广大用户提供学习与参考之用。所有文本和图片版权归原创网站或作者本人所有,其观点并不代表本站立场。如有任何版权侵犯或转载不当之情况,请与我们取得联系,我们将尽快进行相关处理与修改。感谢您的理解与支持!
请先 登录后发表评论 ~