简体   繁体   中英

create new object within C# Linq syntax on Select

I have a list of Vector2 objects. I want to select a value from each element and sort these values. After that, I want to get the lowest value.

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

This code throws an error

CS0029 C# Cannot implicitly convert anonymous type: Sym.Vector2 Vector2, int Value to Sym.Vector2

How can I fix this? I need to setup the Value property based on the current element.

UPDATE: You are using this to implement the A-star algorithm. Though the approach you are using works, you will probably be better off if you implement a priority queue; you can get significant performance wins by doing so.


It's unclear why you are going to the trouble of creating a sequence of anonymous types in the first place; why not simply write:

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

?

Note that though this is more efficient than what you wrote, is not as efficient as it could be .

What you really want is the smallest item in a set. Unfortunately, that is not an operation that is provided in the standard sequence library.

Let's fix that.

What we want to write is:

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

Let's suppose the cost is a double, to make it easier.

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

And we're done. We don't have to sort a list to find the smallest item!

Exercise : Suppose the cost function does not return a double. Can you genericize MinBy further, so that it can take any cost function?

After this line:

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

You have a collection of anonymous objects with Vector2 and Value properties. This is not a Vector2 .

At the end when you use First you are still selecting the first of those anonymous objects. It's not a Vector2 , so you can't assign it to a variable of type Vector2 .

But if you just change it to:

.First().Vector2

That will get the Vector2 property of the first anonymous object you selected. This is a Vector2 , so it should be assignable to cheapestCellPosition

Although it's not the easiest thing to read, but your error is telling you this:

CS0029 C# Cannot implicitly convert anonymous type: Sym.Vector2 Vector2, int Value to Sym.Vector2

It's telling you it can't convert the anonymous type with a Vector2 property of type Sym.Vector2 and a Value property of type int to type Sym.Vector2

The problem is that you're selecting the anonymous type which has both a Vector2 property and a Value property, but then you're trying to assign the first one to a Vector2 type.

If openCells is indeed an IEnumerable<Vector2> , then you can just remove the Select and put your ordering code directly in the OrderBy clause, and then return the First one:

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM