我想知道是否可以用某种方式用LINQ查询替换这个foreach
:(如果可能的话):
在线围栏:https://ideone.com/PQEytf
using System;
using System.Collections.Generic;
using System.Linq;
public class Test
{
public static void Main()
{
// dummy inputs for test sake
var keys = new[] { "key1", "key2" };
var services = new Dictionary<string, Service>
{
{"key1", new Service {Components = new Dictionary<string, string> {{"comp1", "value1"}}}},
{"key2", new Service {Components = new Dictionary<string, string> {{"comp2", "value2"}}}}
};
var serviceComponents = GetServiceComponents(keys, services);
// do something with it
}
public static IEnumerable<ServiceComponent> GetServiceComponents(string[] keys, Dictionary<string, Service> services)
{
var serviceComponents = new List<ServiceComponent>();
// this is the foreach that I want to lose
foreach (
var key in
keys.Select(
name =>
services.FirstOrDefault(
x => x.Key.Equals(name))))
{
serviceComponents.AddRange(key.Value.Components.Select(component => new ServiceComponent
{
Name = key.Key,
Service = component.Key,
Value = component.Value,
}));
}
return serviceComponents.ToArray();
}
}
public class Service
{
public Dictionary<string, string> Components { get; set; }
}
public class ServiceComponent
{
public string Name { get; set; }
public string Service { get; set; }
public string Value { get; set; }
}
答案 0 :(得分:7)
是的,您正在寻找的是SelectMany
。这允许您将序列中的每个项目转换为另一个序列,然后将所有这些序列展平为单个序列。 (通过将所有序列放入列表中,您可以完成相同的操作,无需延迟执行。)
return keys.SelectMany(name => services.FirstOrDefault(x => x.Key.Equals(name))
.Value.Components
.Select(component => new ServiceComponent
{
Name = name.Key,
Service = component.Key,
Value = component.Value,
}))
.ToArray();
话虽如此,此查询正在做的是获取每个密钥,使用线性搜索在服务中查找相应的项目,然后映射结果。您可以使用字典的本机功能来有效地查找每个键的值,而不是使用FirstOrDefault
进行线性搜索:
return keys.SelectMany(key => services[key].Components
.Select(component => new ServiceComponent
{
Name = key,
Service = component.Key,
Value = component.Value,
}))
.ToArray();
答案 1 :(得分:2)
为了扩展@Servy的例子,我经常发现LINQ表达式语法比lambda SelectMany更容易阅读(它转换为相同的东西)。这是他使用查询表达式的查询:
return from key in keys
from component in services[key].Components
select new ServiceComponent
{
Name = key,
Service = component.Key,
Value = component.Value,
}))
.ToArray();
答案 2 :(得分:1)
您需要的是SelectMany
,但是,使用FirstOrDefault
,您自己打开NullReferenceException
(因为任何参考类型的默认值为null
)。如果您NullReferenceException
中的keys
元素不是services
中的关键字时打算SelectMany
,则可以使用其他利用NullReferenceException
的答案。但是,如果您不打算return services.Where(pair => keys.Contains(pair.Key))
.SelectMany(pair =>
pair.Value.Components
.Select(component => new ServiceComponent
{
Name = pair.Key,
Service = component.Key,
Value = component.Value
}))
.ToArray();
,那么您应该使用以下内容:
services
此语句将删除keys
中所有键值不在Components
数组中的键值对,然后将每对ServiceComponent
的每个元素转换为新的SelectMany
对象,ServiceComponent
从新{{1}} s中制作单个平面列表。