在Select上的C#Linq语法中创建新对象

时间:2018-05-02 20:49:52

标签: c# linq

我有一个Vector2个对象的列表。我想从每个元素中选择一个值并对这些值进行排序。在那之后,我想获得最低价值。

Vector2 cheapestCellPosition = openCells.Select(x => new {
        Vector2 = x,
        Value = GetCostToTarget(x, targetPosition) + GetCell(x).Cost.GetValueOrDefault()
    })
    .OrderBy(x => x.Value)
    .First();

此代码抛出错误

  

CS0029 C#无法隐式转换匿名类型:Sym.Vector2   Vector2,int值为Sym.Vector2

我该如何解决这个问题?我需要根据当前元素设置Value属性。

3 个答案:

答案 0 :(得分:5)

更新:您正在使用它来实现A-star算法。虽然您使用的方法有效,但如果实现优先级队列,可能会更好;这样做可以获得显着的性能提升。

目前还不清楚你为什么要首先创建一系列匿名类型的麻烦;为什么不简单地写:

Vector2 cheapestCellPosition = openCells
  .OrderBy(x => GetCostToTarget(x, targetPosition) + GetCell(x).Cost.GetValueOrDefault())
  .First();

请注意,虽然这比你写的更有效,但效率不如

你真正想要的是一组中最小的项目。不幸的是,这不是标准序列库中提供的操作。

让我们解决这个问题。

我们想写的是:

Vector2 cheapestCellPosition = openCells
  .MinBy(x => GetCostToTarget(x, targetPosition) + GetCell(x).Cost.GetValueOrDefault());

让我们假设成本是双倍的,以使其更容易。

static class Extensions 
{
  public static T MinBy(this IEnumerable<T> items, Func<T, double> cost) 
  {
    T minItem = default(T);
    double? minCost = null;
    foreach(T item in items) 
    {
      double current = cost(item);
      if (minCost == null || current < minCost)
      {
        minCost = current;
        minItem = item;
      }
    }
    if (minCost == null) throw InvalidOperationException();
    return minItem;
  }
}

我们已经完成了。我们不必对列表进行排序以找到最小的项目!

练习:假设费用函数不返回double。您是否可以进一步对MinBy进行通用化,以便它可以承担任何成本函数?

答案 1 :(得分:3)

这一行之后:

.Select(x => new { Vector2 = x, Value = GetCostToTarget(x, targetPosition) + GetCell(x).Cost.GetValueOrDefault() })

您拥有一组具有Vector2Value属性的匿名对象。这是 a Vector2

当您使用First时,您仍然会选择这些匿名对象的第一个。它不是Vector2,因此您无法将其分配给Vector2类型的变量。

但是如果你把它改成:

.First().Vector2

这将获得您选择的第一个匿名对象的Vector2属性。此 一个Vector2,因此应该可以分配给cheapestCellPosition

虽然这不是最容易阅读的内容,但是你的错误告诉了你:

  

CS0029 C#无法隐式转换匿名类型:Sym.Vector2 Vector2,int值为Sym.Vector2

它告诉你它不能将匿名类型转换为类型为Vector2的{​​{1}}属性和类型为Sym.Vector2的{​​{1}}属性,以键入Value }

答案 2 :(得分:0)

问题是您选择的匿名类型同时具有Vector2属性和Value属性,但是您尝试将第一个属性分配给{{1} }类型。

如果Vector2确实是openCells,那么您可以删除IEnumerable<Vector2>并将您的订购代码直接放在Select子句中,然后返回{{ 1}}一:

OrderBy