如何实现基于接口参数类型的灵活泛型返回类型适配?
解决基于接口实现类型返回对应关联类型的问题
看起来你卡在了泛型方法的类型推断和灵活返回上——毕竟当你传入IReSizeableGrid的不同实现时,想要返回对应的坐标类型(比如Hex对应HexGrid,Quad对应QuadGrid),直接用泛型的话编译器没法在运行时自动匹配类型,而且还会有类型转换的编译错误。
我给你几个可行的方案,从快速修复到更优雅的扩展性方案:
方案1:给泛型方法添加类型约束+显式转换(快速修复)
如果你的调用方明确知道传入的gridData类型,并且能指定T,可以给泛型方法加上类型约束,同时在返回时显式转换为T:
public static T GridPosition<T>(IReSizeableGrid gridData) where T : class // 根据实际返回类型调整约束,值类型可改用struct { if (gridData is HexGrid hexGrid) { var hexPos = HexLibrary.WorldToHex(WorldPoint(Input.mousePosition, GroundPlane), hexGrid); return hexPos as T; } if (gridData is QuadGrid quadGrid) { var quadPos = QuadLibrary.WorldToQuad(WorldPoint(Input.mousePosition, GroundPlane), quadGrid); return quadPos as T; } // 处理未知类型的情况 throw new ArgumentException($"Unsupported grid type: {gridData.GetType().Name}"); }
调用时需要显式指定T:
var hexGrid = new HexGrid(); Hex hexPos = GridPosition<Hex>(hexGrid); var quadGrid = new QuadGrid(); Quad quadPos = GridPosition<Quad>(quadGrid);
但这个方案的缺点是调用方必须提前知道类型,不够灵活,如果传入的是未知的IReSizeableGrid实例,就没法直接使用了。
方案2:使用访问者模式(优雅且可扩展)
这是处理这种“根据接口实现类型返回不同类型”场景的经典方案,符合开闭原则——以后新增IReSizeableGrid的实现时,不需要修改这个坐标转换方法,只需要扩展访问者接口。
首先定义访问者接口:
public interface IGridVisitor<T> { T VisitHexGrid(HexGrid hexGrid); T VisitQuadGrid(QuadGrid quadGrid); // 新增Grid类型时在这里添加对应的Visit方法 }
然后修改IReSizeableGrid接口,添加Accept方法:
public interface IReSizeableGrid { // 原有的接口方法... T Accept<T>(IGridVisitor<T> visitor); }
接着让每个Grid实现类实现Accept方法:
public class HexGrid : IReSizeableGrid { // 原有的类成员... public T Accept<T>(IGridVisitor<T> visitor) { return visitor.VisitHexGrid(this); } } public class QuadGrid : IReSizeableGrid { // 原有的类成员... public T Accept<T>(IGridVisitor<T> visitor) { return visitor.VisitQuadGrid(this); } }
然后实现坐标转换的访问者:
public class GridPositionVisitor : IGridVisitor<object> { private readonly Vector3 _worldPoint; private readonly Plane _groundPlane; public GridPositionVisitor(Vector3 worldPoint, Plane groundPlane) { _worldPoint = worldPoint; _groundPlane = groundPlane; } public object VisitHexGrid(HexGrid hexGrid) { return HexLibrary.WorldToHex(_worldPoint, hexGrid); } public object VisitQuadGrid(QuadGrid quadGrid) { return QuadLibrary.WorldToQuad(_worldPoint, quadGrid); } }
最后,你的坐标获取方法就可以简化成:
public static object GetGridPosition(IReSizeableGrid gridData) { var worldPoint = WorldPoint(Input.mousePosition, GroundPlane); var visitor = new GridPositionVisitor(worldPoint, GroundPlane); return gridData.Accept(visitor); }
如果想要强类型返回,可以在调用后转换:
var hexGrid = new HexGrid(); Hex hexPos = (Hex)GetGridPosition(hexGrid);
这个方案的优势是扩展性极强,以后新增比如OctGrid,只需要:
- 在
IGridVisitor<T>里添加VisitOctGrid(OctGrid octGrid) - 让
OctGrid实现Accept方法 - 在
GridPositionVisitor里实现对应的转换逻辑
完全不需要修改原有的GetGridPosition方法。
方案3:使用返回类型为动态(简单但不推荐大型项目)
如果你只是快速实现,不介意放弃编译时类型检查,可以用dynamic:
public static dynamic GridPosition(IReSizeableGrid gridData) { if (gridData is HexGrid hexGrid) { return HexLibrary.WorldToHex(WorldPoint(Input.mousePosition, GroundPlane), hexGrid); } if (gridData is QuadGrid quadGrid) { return QuadLibrary.WorldToQuad(WorldPoint(Input.mousePosition, GroundPlane), quadGrid); } throw new ArgumentException($"Unsupported grid type: {gridData.GetType().Name}"); }
调用的时候直接用:
var gridPos = GridPosition(someGrid); // 编译器会在运行时推断类型,但没有编译时检查,出错会在运行时抛异常
不过这个方案不推荐在大型项目中使用,因为丢失了编译时类型安全,调试起来也比较麻烦。
内容的提问来源于stack exchange,提问作者WDUK
相关产品推荐
相关产品推荐

