使用LINQ检索多个标量值

时间:2018-04-10 20:57:22

标签: c# .net linq

我想知道当需要来自多个记录的单个值时,是否有一些聪明的方法可以使用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通过枚举只传递一次来获取所有内容?

如果您想浏览我的代码,可以使用link to a Fiddle

(我知道我可以使用字典或其他数据结构来解决这个特殊问题;这只是一个例子。)

2 个答案:

答案 0 :(得分:4)

通常情况下,您无法在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对象的状态。

而且,此外,你需要一个聪明的方式&#39;应该表明这不是它的意思:)

Eric Lippert撰写了一篇很棒的文章,详细信息如下:https://blogs.msdn.microsoft.com/ericlippert/2009/05/18/foreach-vs-foreach/

答案 1 :(得分:3)

如果无法保证来自同一个人的数字按顺序排列,那么您必须枚举该列表,直到找到所有数字。在我看来,这不是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()

我在此假设<bean id="clientCredentialsTokenEndpointFilter" class="org.springframework.security.oauth2.provider.client.ClientCredentialsTokenEndpointFilter"> <property name="authenticationManager" ref="authenticationManager" /> </bean> 返回所有人的所有电话。