c# - 将嵌套的 foreach 循环转换为 LINQ

标签 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);
    }
  }
}

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

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; }
}

最佳答案

我认为这实际上不是转换为 LINQ 的良好候选者 - 至少不是目前的形式。

是的,您有一个嵌套的 foreach 循环 - 但您在顶级 foreach 循环中执行其他操作,因此它不是只是包含嵌套的易于转换的形式。

更重要的是,您的代码主体都是关于副作用的,无论是写入控制台还是更改您找到的对象中的值。当你有一个复杂的查询并且你想遍历它以依次对每个项目进行操作时,LINQ 非常有用,可能会有副作用......但是你的查询并不真的很复杂,所以你不会得到太多好处。

可以做的一件事是为BlueprintProduct 提供一个包含Id 的通用接口(interface)姓名。然后,您可以编写一个方法,根据对每个产品和蓝图的查询,通过 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;
        }
    }
}

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

关于c# - 将嵌套的 foreach 循环转换为 LINQ,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/4666138/

相关文章:

c# - 有序数据结构,允许有效删除重复项

c# - 在循环中创建大量列表的性能问题

c# - Razor 引擎对我的内联代码不满意

asp.net-mvc-3 - 在 LINQ 查询中调用方法

php - Foreach $item 不显示

javascript - 使用 forEach 遍历对象(不常见 for)

c# - 使用 SMO 找到要还原的备份文件

c# - 如何用 C# 中的函数语句替换 for 循环?

c# - 如何将 LINQ 与具有并行数组的类一起使用?

foreach - 显示并执行