将嵌套的foreach循环转换为LINQ

Ian*_*emp 3 c# linq foreach

我编写了以下代码来设置各种类的属性.它有效,但我的新一年的解决方案之一是尽可能多地使用LINQ,显然这段代码没有.有没有办法以"纯LINQ"格式重写它,最好不使用foreach循环?(如果可以在单个LINQ语句中完成,则更好 - 子语句很好.)

我试过玩,join但这并没有把我带到任何地方,因此我要求回答这个问题 - 最好没有解释,因为我更喜欢"反编译"解决方案以弄清楚它是如何工作的.(正如你可能猜到的那样,我目前在阅读LINQ方面比写它更好,但我打算改变它...)

 public void PopulateBlueprints(IEnumerable<Blueprint> blueprints)
 {
   XElement items = GetItems();
   // item id => name mappings
   var itemsDictionary = (
     from item in items
     select new
     {
       Id = Convert.ToUInt32(item.Attribute("id").Value),
       Name = item.Attribute("name").Value,
     }).Distinct().ToDictionary(pair => pair.Id, pair => pair.Name);

  foreach (var blueprint in blueprints)
  {
    foreach (var material in blueprint.Input.Keys)
    {
      if (itemsDictionary.ContainsKey(material.Id))
      {
        material.Name = itemsDictionary[material.Id];
      }
      else
      {
        Console.WriteLine("m: " + material.Id);
      }
    }

    if (itemsDictionary.ContainsKey(blueprint.Output.Id))
    {
      blueprint.Output.Name = itemsDictionary[blueprint.Output.Id];
    }
    else
    {
      Console.WriteLine("b: " + blueprint.Output.Id);
    }
  }
}
Run Code Online (Sandbox Code Playgroud)

必要类的定义如下; 它们只是数据的容器,我已经删除了与我的问题无关的所有内容:

public class Material
{
  public uint Id { get; set; }

  public string Name { get; set; }
}

public class Product
{
  public uint Id { get; set; }

  public string Name { get; set; }
}

public class Blueprint
{
  public IDictionary<Material, uint> Input { get; set; }

  public Product Output { get; set; }
}
Run Code Online (Sandbox Code Playgroud)

Jon*_*eet 6

我不认为这实际上是转换为LINQ的好选择 - 至少目前的形式不是这样.

是的,你有一个嵌套的foreach循环 - 但是你在顶级foreach循环中做了其他的事情,所以它不是易于转换的形式,包含嵌套.

更重要的是,代码的主体是关于副作用的,无论是写入控制台还是更改您找到的对象中的值.LINQ很棒,当你有一个复杂的查询,你想循环它依次对每个项目采取行动,可能有副作用......但你的查询并不是很复杂,所以你不会得到太多效益.

有一两件事你可以做的就是给BlueprintProduct包含一个通用的接口IdName.然后,您可以itemsDictionary根据每个查询编写一个方法来更新产品和蓝图:

UpdateNames(itemsDictionary, blueprints);
UpdateNames(itemsDictionary, blueprints.SelectMany(x => x.Input.Keys));

...

private static void UpdateNames<TSource>(Dictionary<string, string> idMap,
    IEnumerable<TSource> source) where TSource : INameAndId
{
    foreach (TSource item in source)
    {
        string name;
        if (idMap.TryGetValue(item.Id, out name))
        {
            item.Name = name;
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

这假设您实际上不需要控制台输出.如果这样做,您可以始终传入适当的前缀并在方法中添加"else"块.请注意,我已经使用TryGetValue而不是在每次迭代的字典上执行两次查找.