我想知道当需要来自多个记录的单个值时,是否有一些聪明的方法可以使用 LINQ 从可枚举项中检索数据。
例如,假设您有一个人有三个不同的电话字段:
public class Person
{
public Phone HomePhone { get; set; }
public Phone WorkPhone { get; set; }
public Phone CellPhone { get; set; }
}
。但电话列表以规范化格式存储:
public enum PhoneType
{
Home, Work, Cell
}
public class Phone
{
public PhoneType Type { get; set; }
public string Number { get; set; }
}
static public IEnumerable<Phone> GetPhoneList()
{
yield return new Phone { Type = PhoneType.Home, Number = "8005551212" };
yield return new Phone { Type = PhoneType.Work, Number = "8005551313" };
yield return new Phone { Type = PhoneType.Cell, Number = "8005551414" };
}
如果需要填充 Person,可以编写一个循环,并一次性获取所需的所有内容:
public static Person GetPerson1()
{
var result = new Person();
foreach (var ph in GetPhoneList())
{
switch (ph.Type)
{
case PhoneType.Home: result.HomePhone = ph; break;
case PhoneType.Work: result.WorkPhone = ph; break;
case PhoneType.Cell: result.CellPhone = ph; break;
}
}
return result;
}
但是,如果要使用 LINQ,似乎可能需要三次传递:
public static Person GetPerson2()
{
return new Person
{
HomePhone = GetPhoneList().Single( ph => ph.Type == PhoneType.Home ),
WorkPhone = GetPhoneList().Single( ph => ph.Type == PhoneType.Work ),
CellPhone = GetPhoneList().Single( ph => ph.Type == PhoneType.Cell )
};
}
有没有一种聪明的方法可以使用 LINQ 只需一次枚举即可获得所有内容?
如果你想探索我的代码,这里有一个小提琴的链接。
(我知道我可以使用字典或其他数据结构来解决这个特定问题;这只是一个例子。
通常,您无法在 LINQ 中执行此操作。
如果确实需要,可以创建 Foreach 扩展方法,并执行与GetPerson1
方法相同的操作。
public static class Ext
{
public static void Foreach<T>(this IEnumerable<T> e, Action<T> action)
{
foreach (T item in e)
{
action(item);
}
}
}
然后
public static Person GetPerson2()
{
var p = new Person();
var pl = GetPhoneList();
pl.Foreach(ph =>
{
switch (ph.Type)
{
case PhoneType.Home: p.HomePhone = ph; break;
case PhoneType.Work: p.WorkPhone = ph; break;
case PhoneType.Cell: p.CellPhone = ph; break;
}
});
return p;
}
但你真的不应该。LINQ 旨在对 IEnumerables(逐项(进行操作,并且 LINQ 函数应该没有副作用,而 foreach 循环和 Foreach 扩展方法只会产生副作用,从而更改 Person 对象的状态。
而且,此外,你需要一种"聪明的方法"这一事实应该表明这不是它应该被使用的方式:)
埃里克·利珀特(Eric Lippert(有一篇很棒的文章,其中有更多细节:https://blogs.msdn.microsoft.com/ericlippert/2009/05/18/foreach-vs-foreach/
如果不能保证来自同一个人的号码按顺序排列,那么你必须枚举列表,直到找到所有号码。在我看来,这似乎不是 LINQ 的良好候选项,LINQ 的目的是使代码更具可读性。你的foreach
很好,当找到所有数字时,我会打破循环。
如果你想列举所有人,而不仅仅是一个人,那么Dictionary
方法可能是最有效的。 GroupBy
内部使用字典,您可以使用GroupBy
收集属于一个人的所有数字,然后Aggregate
从中Person
。假设有一些属性Phone.PersonID
,并且还有Person.PersonID
,那么你会有这样的东西:
GetPhoneList()
.GroupBy(x => x.PersonID)
.Select(x => x.Aggregate(new Person() { PersonID = x.Key },
(person, phone) =>
{
switch (phone.Type)
{
case PhoneType.Home: person.HomePhone = phone; break;
case PhoneType.Work: person.WorkPhone = phone; break;
case PhoneType.Cell: person.CellPhone = phone; break;
}
return person;
}));
我在这里假设GetPhoneList()
返回所有人的所有电话。