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

如何为实现带共同基类泛型接口的服务编写工厂方法?

如何为实现泛型接口的服务编写工厂方法

现有一组服务,每个都实现了泛型接口IHandleStuff<T>,其中T继承自共同基类Base。需要编写工厂方法返回对应服务实例,但无法确定合适的返回类型,示例代码如下:

public abstract class Base
{
    public int BaseProp { get; set; }
}

public class Derived1 : Base
{
    public int DerivedProp1 { get; set; }
}

public class Derived2 : Base
{
    public int DerivedProp2 { get; set; }
}

public interface IHandleStuff<T> where T : Base
{
    T GetStuff();
    void DoStuff(T t);
}

public class Service1 : IHandleStuff<Derived1>
{
    public Derived1 GetStuff() => new();
    public void DoStuff(Derived1 t){}
}

public class Service2 : IHandleStuff<Derived2>
{
    public Derived2 GetStuff() => new();
    public void DoStuff(Derived2 t){}
}

public class Consumer
{
    public void DoStuff(Base t)
    {
        var service = GetServiceFor(t);
        service.DoStuff(t);
    }

    private IHandleStuff<Base> GetServiceFor(Base t)
    {
        return t.BaseProp switch
        {
            1 => new Service1(), // 编译错误:无法将Service1转换为IHandleStuff<Base>
            2 => new Service2(), // 强制转换编译通过但运行时失败
            _ => throw new ArgumentOutOfRangeException()
        };
    }
}

补充说明:曾尝试将Consumer中的DoStuff和GetServiceFor改为泛型方法,但实际场景中必须从仅知晓Base类型的非泛型方法调用逻辑,因此该方案不适用。


解决方案

方案1:引入非泛型基础接口

定义一个非泛型的IHandleStuff接口作为泛型接口的基接口,通过显式实现非泛型方法来适配类型转换,既保证类型安全,又满足非泛型调用需求。

// 新增非泛型接口
public interface IHandleStuff
{
    Base GetStuff();
    void DoStuff(Base t);
}

// 修改泛型接口继承非泛型接口
public interface IHandleStuff<T> : IHandleStuff where T : Base
{
    new T GetStuff(); // 重写GetStuff的返回类型
    void DoStuff(T t);
}

// 调整服务类,显式实现非泛型接口
public class Service1 : IHandleStuff<Derived1>
{
    public Derived1 GetStuff() => new();
    public void DoStuff(Derived1 t) { }

    // 显式实现非泛型接口方法
    Base IHandleStuff.GetStuff() => GetStuff();
    void IHandleStuff.DoStuff(Base t) => DoStuff((Derived1)t);
}

public class Service2 : IHandleStuff<Derived2>
{
    public Derived2 GetStuff() => new();
    public void DoStuff(Derived2 t) { }

    Base IHandleStuff.GetStuff() => GetStuff();
    void IHandleStuff.DoStuff(Base t) => DoStuff((Derived2)t);
}

// 修改Consumer的工厂方法返回类型
public class Consumer
{
    public void DoStuff(Base t)
    {
        var service = GetServiceFor(t);
        service.DoStuff(t);
    }

    private IHandleStuff GetServiceFor(Base t)
    {
        return t.BaseProp switch
        {
            1 => new Service1(),
            2 => new Service2(),
            _ => throw new ArgumentOutOfRangeException()
        };
    }
}

此方案中,工厂方法返回非泛型接口实例,调用DoStuff时会触发显式实现的方法,在工厂逻辑正确的前提下,强制转换不会出错。

方案2:使用动态类型(Dynamic)

利用dynamic绕过编译时类型检查,直接在运行时解析方法调用,无需修改原有接口结构。

public class Consumer
{
    public void DoStuff(Base t)
    {
        dynamic service = GetServiceFor(t);
        service.DoStuff(t);
    }

    private object GetServiceFor(Base t)
    {
        return t.BaseProp switch
        {
            1 => new Service1(),
            2 => new Service2(),
            _ => throw new ArgumentOutOfRangeException()
        };
    }
}

注意:该方式失去编译时类型校验,若工厂返回的服务与t实际类型不匹配,会抛出运行时异常,需确保类型映射逻辑正确。

方案3:反射调用

通过反射创建服务实例并调用对应方法,适合服务数量多、无需手动维护类型转换的场景。

public class Consumer
{
    public void DoStuff(Base t)
    {
        Type serviceType = t.BaseProp switch
        {
            1 => typeof(Service1),
            2 => typeof(Service2),
            _ => throw new ArgumentOutOfRangeException()
        };

        // 创建服务实例
        object service = Activator.CreateInstance(serviceType);
        // 获取并调用DoStuff方法
        MethodInfo doStuffMethod = serviceType.GetMethod(nameof(IHandleStuff<Base>.DoStuff));
        doStuffMethod.Invoke(service, new[] { t });
    }
}

此方案灵活性高,但性能略低于前两种,同样依赖类型映射逻辑的准确性。


内容的提问来源于stack exchange,提问作者Ian Horwill

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.19 22:45:13