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

C#中如何为Shape派生类列表匹配对应的泛型IDrawService实现

C#中如何为Shape派生类列表匹配对应的泛型IDrawService实现

这个问题我之前也碰到过——当手里拿着一堆基类Shape的实例,却要调用每个实例对应的泛型IDrawService<T>时,直接用基类遍历很容易栽在类型转换的坑里,你遇到的InvalidCastException就是因为泛型接口的不变性导致的:IDrawService<Triangle>和IDrawService<Shape>是完全不同的类型,没法直接转换。

下面给你几个实用的解决方案,你可以根据自己的业务场景选:

方案一:用字典+反射动态调用(无需修改Shape类)

这种方法适合不想改动现有Shape子类的情况,核心是先把所有服务按Shape类型缓存起来,再用反射调用对应方法:

  1. 先给泛型接口加一个非泛型的基接口,方便统一注入:
// 新增非泛型标记接口
public interface IDrawService { }

// 让泛型接口继承它
public interface IDrawService<T> : IDrawService where T : Shape
{
    T DrawShape(T shape);
}
  1. 在你的业务类里注入所有IDrawService实现,并用字典缓存类型映射:
public class ShapeDrawer
{
    private readonly Dictionary<Type, IDrawService> _serviceMap;

    // 构造函数注入所有Draw服务
    public ShapeDrawer(IEnumerable<IDrawService> drawServices)
    {
        _serviceMap = drawServices.ToDictionary(service => 
            // 获取服务对应的Shape类型
            service.GetType().GetInterfaces()
                .First(i => i.IsGenericType && i.GetGenericTypeDefinition() == typeof(IDrawService<>))
                .GetGenericArguments()[0]);
    }

    public void DrawAllShapes(IEnumerable<Shape> shapes)
    {
        foreach (var shape in shapes)
        {
            if (_serviceMap.TryGetValue(shape.GetType(), out var service))
            {
                // 用反射调用DrawShape方法
                var drawMethod = service.GetType().GetMethod(nameof(IDrawService<Shape>.DrawShape));
                drawMethod?.Invoke(service, new[] { shape });
            }
        }
    }
}

这个方法的优点是不用修改现有Shape子类,缺点是用到了反射,性能上会有一点点损耗,但普通业务场景完全够用。

方案二:双重分发(类型安全,无反射)

如果允许修改Shape类,这种方法更优雅,利用双重分发让子类自己找到对应的服务:

  1. 先定义一个服务提供者接口(也可以直接用.NET自带的IServiceProvider):
public interface IDrawServiceProvider
{
    T GetService<T>() where T : class;
}
  1. 在Shape基类里添加抽象方法,让子类实现自己的绘制逻辑:
public abstract class Shape
{
    public int Id { get; set; }
    // 新增抽象方法,让子类自己处理服务调用
    public abstract void DrawWith(IDrawServiceProvider provider);
}

public class Triangle : Shape
{
    public int TriangleSpecificProps { get; set; }

    public override void DrawWith(IDrawServiceProvider provider)
    {
        var service = provider.GetService<IDrawService<Triangle>>();
        service?.DrawShape(this);
    }
}

public class Square : Shape
{
    public int SquareSpecificProps { get; set; }

    public override void DrawWith(IDrawServiceProvider provider)
    {
        var service = provider.GetService<IDrawService<Square>>();
        service?.DrawShape(this);
    }
}
  1. 然后在业务类里注入IDrawServiceProvider,直接调用即可:
public class ShapeDrawer
{
    private readonly IDrawServiceProvider _serviceProvider;

    public ShapeDrawer(IDrawServiceProvider serviceProvider)
    {
        _serviceProvider = serviceProvider;
    }

    public void DrawAllShapes(IEnumerable<Shape> shapes)
    {
        foreach (var shape in shapes)
        {
            shape.DrawWith(_serviceProvider);
        }
    }
}

这个方法完全类型安全,没有反射,缺点是Shape子类需要知道IDrawService的存在,可能稍微违反单一职责,但逻辑清晰,扩展性好。

方案三:模式匹配+泛型方法(适合子类较少的场景)

如果你的Shape子类不多,用模式匹配直接分情况处理也很简单:

  1. 业务类注入所有IDrawService实现:
public class ShapeDrawer
{
    private readonly IEnumerable<IDrawService> _drawServices;

    public ShapeDrawer(IEnumerable<IDrawService> drawServices)
    {
        _drawServices = drawServices;
    }

    private void DrawShape<T>(T shape) where T : Shape
    {
        var service = _drawServices.OfType<IDrawService<T>>().FirstOrDefault();
        service?.DrawShape(shape);
    }

    public void DrawAllShapes(IEnumerable<Shape> shapes)
    {
        foreach (var shape in shapes)
        {
            switch (shape)
            {
                case Triangle triangle:
                    DrawShape(triangle);
                    break;
                case Square square:
                    DrawShape(square);
                    break;
                // 新增子类时在这里加case
                default:
                    // 处理未知Shape类型
                    break;
            }
        }
    }
}

这个方法类型安全,代码直观,缺点是新增Shape子类时需要修改switch语句,扩展性稍差,适合子类数量少的场景。


备注:内容来源于stack exchange,提问作者Ryan Gaudion

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.23 10:35:29