我有以下路径查找代码:
public interface INode
{
List<INode> GetNeighbours();
float GetMovementCost(INode other);
}
public static class Pathfinder
{
public static List<INode> FindPath(INode origin, INode target)
{
var path = new List<INode>();
var cameFrom = new Dictionary<INode, INode>();
var costToNode = new Dictionary<INode, float>();
var frontier = new PriorityQueue<INode>();
Dictionary<INode, float> costSoFar = new Dictionary<INode, float>();
frontier.Enqueue(origin, 0);
costSoFar[frontier[0].Element] = 0;
while (frontier.Count > 0)
{
INode current = frontier.Dequeue();
foreach (var neighbour in current.GetNeighbours())
{
float newCost = costSoFar[current] +
current.GetMovementCost(neighbour);
if (!costSoFar.ContainsKey(neighbour) || newCost < costSoFar[neighbour])
{
costSoFar[neighbour] = newCost;
frontier.Enqueue(neighbour, newCost);
cameFrom[neighbour] = current;
}
}
if (current == target)
{
break;
}
}
//build path
INode nn = target;
while (nn != origin)
{
path.Add(nn);
nn = cameFrom[nn];
}
path.Reverse();
return path;
}
public struct PriorityElement<T>
{
public T Element;
public float Priority;
public PriorityElement(T element, float priority)
{
if (priority < 0) throw new Exception("Priorities must be non-negative");
Element = element;
Priority = priority;
}
}
public class PriorityQueue<T> : List<PriorityElement<T>>
{
public void Enqueue(T element, float priority)
{
if (this.Count == 0)
{
this.Insert(0, new PriorityElement<T>(element, priority));
return;
}
if (priority > this.Last().Priority)
{
this.Add(new PriorityElement<T>(element, priority));
return;
}
var firstLowerThan = this.First(p => priority <= p.Priority);
this.Insert(IndexOf(firstLowerThan), new PriorityElement<T>(element, priority));
}
public T Dequeue()
{
if (this.Count == 0)
{
throw new Exception("Cannot dequeue, queue is empty");
}
var ret = this.First();
this.Remove(ret);
return ret.Element;
}
}
}
这样可以正常工作,但真正讨厌的是它返回一个INode,而不是那些暗示INode的东西,所以我必须把它重新投入并且它不是非常类型安全的。
我正在尝试实现此代码的通用版本,类似于:
public static List<Node> FindPath<Node>(Node origin, Node target) where Node : INode<Node>
然而,这显然不起作用,因为List<INode> GetNeighbours();
在不使用协方差和逆变的情况下无法返回更多派生类型。在这里实施逆变是棘手的。
有人可以帮我重新定义我的INode界面,以便上述签名有效吗?主要问题是GetNeighbours()
需要返回一个移动派生类型的INode列表。
有些事情:
public interface INode
{
List<T> GetNeighbours<T>() where T : INode;
float GetMovementCost(INode other);
}
答案 0 :(得分:2)
您的FindPath
应该使用T
类型T : class, INode
的参数,而不是明确地使用INode
个参数。然后,在方法内部,您应该引用T
而不是INode
。例如,您可以写:
public static List<T> FindPath<T>(T origin, T target) where T : class, INode
{
var path = new List<T>();
var cameFrom = new Dictionary<T, T>();
var costToNode = new Dictionary<T, float>();
var frontier = new PriorityQueue<T>();
Dictionary<T, float> costSoFar = new Dictionary<T, float>();
frontier.Enqueue(origin, 0);
costSoFar[frontier[0].Element] = 0;
while (frontier.Count > 0)
{
T current = frontier.Dequeue();
foreach (var neighbour in current.GetNeighbours<T>())
{
float newCost = costSoFar[current] +
current.GetMovementCost(neighbour);
if (!costSoFar.ContainsKey(neighbour) || newCost < costSoFar[neighbour])
{
costSoFar[neighbour] = newCost;
frontier.Enqueue(neighbour, newCost);
cameFrom[neighbour] = current;
}
}
if (current == target)
{
break;
}
}
//build path
T nn = target;
while (nn != origin)
{
path.Add(nn);
nn = cameFrom[nn];
}
path.Reverse();
return path;
}
然后只需将您的界面更改为:
public interface INode
{
List<T> GetNeighbours<T>() where T : INode;
float GetMovementCost(INode other);
}