You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何实现基于接口参数类型的灵活泛型返回类型适配?

解决基于接口实现类型返回对应关联类型的问题

看起来你卡在了泛型方法的类型推断和灵活返回上——毕竟当你传入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,只需要:

  1. 在IGridVisitor<T>里添加VisitOctGrid(OctGrid octGrid)
  2. 让OctGrid实现Accept方法
  3. 在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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.21 04:18:03