如何让泛型Grid类兼容实现IPathNode接口的多种网格对象
这个问题我之前也碰到过,核心问题很明确:你不能直接实例化接口IPathNode,必须实例化它的具体实现类。而你的需求是让PathFinding类能适配任意实现了IPathNode的网格对象,那我们可以通过让PathFinding支持泛型或者传入节点创建委托这两种方式来解决,下面具体说:
方案一:把PathFinding改成泛型类(推荐,类型更安全)
首先,我们先完善IPathNode的定义——因为不同的节点实现类对应的Grid类型是自身的泛型,所以把接口也改成泛型会更严谨:
// 泛型接口,约束T必须是实现了该接口的类型 public interface IPathNode<T> where T : IPathNode<T> { Grid<T> Grid { get; set; } int X { get; set; } int Y { get; set; } }
然后让你的PathNode(以及其他节点类)实现这个泛型接口:
public class PathNode : IPathNode<PathNode> { public Grid<PathNode> Grid { get; set; } public int X { get; set; } public int Y { get; set; } public PathNode(Grid<PathNode> grid, int x, int y) { Grid = grid; X = x; Y = y; } // 其他方法... } // 比如新增一个带权重的节点类 public class WeightedPathNode : IPathNode<WeightedPathNode> { public Grid<WeightedPathNode> Grid { get; set; } public int X { get; set; } public int Y { get; set; } public int MovementWeight { get; set; } // 新增的权重属性 public WeightedPathNode(Grid<WeightedPathNode> grid, int x, int y) { Grid = grid; X = x; Y = y; MovementWeight = 1; } }
接下来修改PathFinding为泛型类,约束类型必须实现IPathNode<T>,同时通过构造函数接收一个创建具体节点的委托:
public class PathFinding<TNode> where TNode : IPathNode<TNode> { public Grid<TNode> Grille { get; set; } // 接收创建节点的委托,让调用方决定具体实例化哪个节点类 public PathFinding(int width, int height, Func<Grid<TNode>, int, int, TNode> createNode) { Grille = new Grid<TNode>(width, height, createNode); } // 你的寻路方法可以直接使用TNode,因为它已经约束为IPathNode<T> public List<TNode> FindPath(int startX, int startY, int endX, int endY) { // 寻路逻辑,比如访问TNode的X/Y/Grid属性都是安全的 // ... return new List<TNode>(); } }
使用的时候就可以灵活传入不同的节点类型:
// 使用普通PathNode var basicPathFinding = new PathFinding<PathNode>(10, 10, (grid, x, y) => new PathNode(grid, x, y)); // 使用带权重的WeightedPathNode var weightedPathFinding = new PathFinding<WeightedPathNode>(10, 10, (grid, x, y) => new WeightedPathNode(grid, x, y));
这种方案的好处是类型完全安全,不需要任何强制转换,而且扩展性极强,新增节点类只需要实现IPathNode<T>即可。
方案二:让PathFinding非泛型,通过委托传入节点创建逻辑
如果你不想把PathFinding改成泛型,也可以让它直接使用Grid<IPathNode>,但需要在构造时传入创建具体节点的委托,同时注意处理Grid的类型转换(因为Grid<IPathNode>和Grid<具体节点类>是不变的泛型类型):
首先定义非泛型的IPathNode接口:
public interface IPathNode { // 这里Grid可以定义为object,或者如果需要调用Grid的方法,可以再定义一个非泛型的IGrid接口 object Grid { get; set; } int X { get; set; } int Y { get; set; } }
然后修改节点类实现这个接口:
public class PathNode : IPathNode { // 显式实现非泛型接口,同时保留强类型的Grid属性 object IPathNode.Grid { get => Grid; set => Grid = (Grid<PathNode>)value; } public Grid<PathNode> Grid { get; set; } public int X { get; set; } public int Y { get; set; } public PathNode(Grid<PathNode> grid, int x, int y) { Grid = grid; X = x; Y = y; } }
然后修改PathFinding类:
public class PathFinding { public Grid<IPathNode> Grille { get; set; } // 接收创建IPathNode的委托,调用方负责实例化具体的节点类 public PathFinding(int width, int height, Func<Grid<IPathNode>, int, int, IPathNode> createNode) { Grille = new Grid<IPathNode>(width, height, createNode); } }
使用时需要做一下类型转换:
var pathFinding = new PathFinding(10, 10, (grid, x, y) => new PathNode((Grid<PathNode>)(object)grid, x, y));
这种方案的缺点是需要类型转换,容易出现运行时错误,所以更推荐第一种泛型方案。
总结
你之前的错误本质是试图实例化接口,解决的核心就是把节点的创建逻辑交给调用方,而不是在PathFinding内部硬编码。通过泛型约束可以保证类型安全,同时让PathFinding支持任意实现IPathNode的节点类,完美满足你的需求。
备注:内容来源于stack exchange,提问作者AkakiAkaki

