【问题标题】:Derived generic class from generic base class从泛型基类派生的泛型类
【发布时间】:2014-03-22 04:17:30
【问题描述】:

假设以下类

// What I have created ...

public abstract class TaxServiceProvider<T, S>
    where T : TaxServiceProviderConfig
    where S : TaxServiceInfo
{
    protected T Config { get; set; }

    public abstract S GetTax(int zipCode);
}

public abstract class TaxServiceInfo { ... }
public abstract class TaxServiceProviderConfig { ... }

// What I want to create ...

public class SpecialTaxServiceProvider<T, S> : TaxServiceProvider<SpecialTaxServiceProviderConfig, SpecialTaxServiceInfo>
    where T : SpecialTaxServiceProviderConfig
    where S : SpecialTaxServiceInfo
{ ... }
public class SpecialTaxServiceInfo : TaxServiceInfo { ... }
public class SpecialTaxServiceProviderConfig : TaxServiceProviderConfig { ... }

其中TaxServiceInfo 和TaxServiceProviderConfig 用于支持TaxServiceProvider 类。

我想从TaxServiceProvider 创建一个派生类SpecialTaxServiceProvder(非抽象),它也是通用的,与TaxServiceProvider 相同,并采用SpecialTaxServiceInfo 和SpecialTaxServiceProviderConfig 作为类型。

我想在SpecialTaxServiceProvider 中实现GetTax 和Config,以便GetTax 返回类型SpecialTaxServiceInfo 和Config 的类型为SpecialTaxServiceProviderConfig

然后我将创建一个从SpecialTaxServiceProvider 派生的附加类以及从SpecialTaxServiceInfo 和SpecialTaxServiceProviderConfig 派生的类

public class A_SpecialTaxServiceProvider : SpecialTaxServiceProvider<A_SpecialTaxServiceProviderConfig, A_SpecialTaxServiceInfo>
{ ... }
public class A_SpecialTaxServiceProviderConfig : SpecialTaxServiceProviderConfig { ... }
public class A_SpecialTaxServiceInfo : SpecialTaxServiceInfo { ... }

其中,此类的 GetTax 返回类型 A_SpecialTaxServiceInfo,而此类的 Config 的类型为 A_SpecialTaxServiceProviderConfig

我研究了 C# 中的协方差和泛型类型类的语法,但我不确定我想要做的事情在语言中是否不可能,或者我只是不知道设置它的正确方法起来。

【问题讨论】:

  • 您正在将泛型的约束添加到特定的具体类。这背后的原因是什么?
  • 我希望任何派生类都被限制为使用同一继承级别的类。如果一个类从 SpecialTaxServiceProvider 继承,我希望它使用的 Config/Info 类来自继承层次结构的相同(或更具体)“组”或“级别”

标签: c# generics inheritance polymorphism abstract-class


【解决方案1】:
  1. 更改SpecialTaxServiceProvider 并将T 和S 传递给TaxServiceProvider:

    public class SpecialTaxServiceProvider<T, S> : TaxServiceProvider<T, S>
        where T : SpecialTaxServiceProviderConfig
        where S : SpecialTaxServiceInfo
    {
        public override S GetTax(int zipCode)
        {
            return null;
        }
    }
    
  2. 实现A_SpecialTaxServiceProvider 并覆盖GetTax:

    public class A_SpecialTaxServiceProvider : SpecialTaxServiceProvider<A_SpecialTaxServiceProviderConfig, A_SpecialTaxServiceInfo>
    {
        public override A_SpecialTaxServiceInfo GetTax(int zipCode)
        {
            return null;
        }
    }
    

它会阻止你创建

public class A_SpecialTaxServiceProvider : SpecialTaxServiceProvider<A_SpecialTaxServiceProviderConfig, TaxServiceInfo>

出现以下错误:

类型“Project.TaxServiceInfo”不能用作类型 泛型类型或方法中的参数“S” 'Project.SpecialTaxServiceProvider'。没有隐含的 从 'Project.TaxServiceInfo' 到的参考转换 'Project.SpecialTaxServiceInfo'。

【讨论】:

  • 当我进行此更改并尝试在我的 SpecialTaxServiceProvider 类的构造函数中创建我的配置( Config = new SpecialTaxServiceProviderConfig(paraA, ...); )时,VisualStudio 告诉我“无法隐式转换类型” SpecialTaxServiceProviderConfig' to 'T'" 如果我​​将类型转换为 T ( Config = (T)new SpecialTaxServiceProviderConfig(paramA, ...); )错误就消失了,但我不确定这是否是正确的编码方式
  • 不,这不是正确的方法。这就像试图将object 向上转换为string 却不知道它实际上是一个string 实例。你想达到什么目的?
  • 我希望我的 Config 属性的类型和我的 GetTaxes 方法的返回类型(都在 Provider 类中)可以根据我的选择进行定义。如果 B 类继承自 A 类(B 类:A,其中这些类型被定义为具体类,那么从 B 类派生的任何类都必须从 B 派生,其中其类型在同一层次结构中至少具有相同的特定性链(C类:B
【解决方案2】:

我决定在我的构造函数中使用函数委托来允许我传入工厂方法来创建我的泛型类型。

这就是我最终得到的结果

// Tier 1 of my class hierarchy
public abstract class TaxServiceProvider<C, I>
    where C : TaxServiceProviderConfig
    where I : TaxServiceInfo
{
    protected C Config { get; set; }

    public abstract I GetTax(int zipCode);
}

public abstract class TaxServiceInfo {
    public TaxServiceInfo(string param1, string param2, int param3, ect...) {
        ...
    }
}

public abstract class TaxServiceProviderConfig { ... }


// Tier 2 of my class hierarchy
public class DerivedTaxServiceProvider<C, I> : TaxServiceProvider<C, I>
    where C : DerivedTaxServiceProviderConfig
    where I : DerivedTaxServiceInfo
{
    protected Func<S, string, string, int, ect...> Factory;

    public DerivedTaxServiceProvider (C config, Func<I, string, string, int, ect...> factory) {
        Config = config;
        Factory = factory;
    }

    public override I GetTax(int zipCode) {
        ...
        I taxServiceInfo = Factory("param1", "param2", 3, ect...);
        ...
        return I;
    }
}

public class DerivedTaxServiceInfo : TaxServiceInfo {
    public DerivedTaxServiceInfo(string param1, string param2, int param3, ect...) 
        : base(param1, param2, param3, ect...) 
    { ... }
}

public class DerivedTaxServiceProviderConfig : TaxServiceProviderConfig { ... }

// Tier 3 of my class hierarchy
public class ConcreteTaxServiceProvider : DerivedTaxServiceProvider<ConcreteTaxServiceProviderConfig, ConcreteTaxServiceInfo> {
    public ConcreteTaxServiceProvider(ConcreteTaxServiceProviderConfig config, Func<ConcreteTaxServiceInfo, string, string, int, ect...> factory) {
        Config = config;
        Factory = factory;
    }

    public override ConcreteTaxServiceInfo GetTax(int zipCode) {
        return base.GetTax(zipCode);
    }
}

public class ConcreteTaxServiceInfo : DerivedTaxServiceInfo {
    public ConcreteTaxServiceInfo(string param1, string param2, int param3, ect...) 
        : base(param1, param2, param3, ect...) 
    { ... }

    public static ConcreteTaxServiceInfo CreateConcreteTaxServiceInfo(string param1, string param2, int param3, ect...) {
        return new ConcreteTaxServiceInfo(param1, param2, param3, etc...);
    }
}

public class ConcreteTaxServiceProviderConfig : DerivedTaxServiceProviderConfig { ... }

// Implementation of my class hierarchies
public void method() {
    ConcreteTaxServiceProviderConfig() config = new ConcreteTaxServiceProviderConfig();

    ConcreteTaxServiceProvider provider = new ConcreteTaxServiceProvider(config, ConcreteTaxServiceInfo.CreateConcreteTaxServiceInfo);

    ConcreteTaxServiceInfo serviceInfo = provider.GetTax(99939);
}

所以基本上我有 2 个级别的泛型类。第二级 Provider 类覆盖了“GetTax”的基本抽象方法,但我不希望它返回具体类型,因为这样我就无法在从它继承的类中干净地调用该方法。在具体(第 3 级)Provider 中调用“GetTax”时,我必须将派生的 ServiceInfo 转换为 ConcreteServiceInfo 类型。

只要我在 Info 类中有一个与我的委托相匹配的构造函数,无论 Info 类处于哪个子类化级别(低于 2 级),我都可以将它输入我的提供程序并使用 2 级的提供程序类 GetTax 方法。

这个工厂方法在 Provider 构造函数的参数列表中看起来有点难看,创建 Info 类的静态方法看起来很尴尬,但它确实有效!

这是我为提出此解决方案而咨询的一个 SO 问题:C# generics problem - newing up the generic type with parameters in the constructor

【讨论】:

    猜你喜欢
    • 2012-09-08
    • 1970-01-01
    • 2011-10-09
    • 1970-01-01
    • 2010-10-22
    • 2011-05-24
    • 1970-01-01
    • 1970-01-01
    • 2015-01-26
    相关资源
    最近更新 更多