当"递归"时,如何声明公共基类型?仿制药涉及?

时间:2018-03-07 16:12:35

标签: c# generics inheritance variable-declaration

我正在使用C#数据可视化库,它大量使用泛型。我们的应用程序需要以编程方式构建这些图表,包括在运行时更改图表系列。代码如下所示:

builder.Series(series =>
{
    if (seriesDefinition.Type == ChartType.Bar) {
        series.Bar(data).Name(seriesDefinition.Name);
    }
    else if (seriesDefinition.Type == ChartType.Area) {
        series.Area(data).Name(seriesDefinition.Name);
    }
})

Bar()Area()调用是库方法,seriesDefinition对象是我们自己的类,其中包含有关我们要创建的系列的配置信息。

现在,我们的系列定义对象除了名称之外还将具有许多属性,并且所有这些属性都需要应用于构建器中的系列。为避免大量代码重复,我们希望存储Bar()Area()等调用的结果,以便我们可以在if-else块之外应用其余配置。为此,我们需要声明其特定类型。从这些库方法的派生返回类型向后工作,我们得到了这个父类型,它具有我们需要的方法:

public abstract class ChartSeriesBuilderBase<TSeries, TSeriesBuilder> : IHideObjectMembers
    where TSeries : IChartSeries
    where TSeriesBuilder : ChartSeriesBuilderBase<TSeries, TSeriesBuilder>
{
}

现在我需要用适当的类型参数声明它:

builder.Series(series =>
{
    ChartSeriesBuilderBase<IChartSeries, ???> seriesBuilder = null; // <--
    if (seriesDefinition.Type == ChartType.Bar) {
        seriesBuilder = series.Bar(data);
    }
    else if (seriesDefinition.Type == ChartType.Area) {
        seriesBuilder = series.Area(data);
    }
    seriesBuilder.Name(seriesDefinition.Name);
})

我可以用???替换满足此类型声明的内容吗?

编辑:以下是以Bar()Area()的返回类型开头的类层次结构。

// Area() returns this; Bar() returns ChartBarSeriesBuilder
public class ChartAreaSeriesBuilder<T> :
    ChartAreaSeriesBuilderBase<IChartAreaSeries, ChartAreaSeriesBuilder<T>>
    where T : class
{
}

T是该系列的数据项的类型,我可以控制。当我将该类型的对象传递给Area()时,推断出它。

// or ChartBarSeriesBuilderBase
public abstract class ChartAreaSeriesBuilderBase<TSeries, TSeriesBuilder> :
    ChartSeriesBuilderBase<TSeries, TSeriesBuilder>
    where TSeries : IAreaSeries
    where TSeriesBuilder : ChartAreaSeriesBuilderBase<TSeries, TSeriesBuilder>
{
}

最后

public abstract class ChartSeriesBuilderBase<TSeries, TSeriesBuilder> : IHideObjectMembers
    where TSeries : IChartSeries
    where TSeriesBuilder : ChartSeriesBuilderBase<TSeries, TSeriesBuilder>
{
}

1 个答案:

答案 0 :(得分:1)

tl; dr 除非ChartSeriesBuilderBase实现其他一些非通用接口,否则不能这样做。

您可以创建一个继承自ChartSeriesBuilderBase的类,然后对其进行实例化,这样可行,如下所示:

public class MyBuilder : ChartSeriesBuilderBase<IChartSeries, MyBuilder>
{
}

它有点令人困惑,但是在定义MyBuilder时,它能够将自身引用为其基类的泛型类型。

我认为BarArea也以类似的方式定义,因此在它们自己的通用定义中引用它们。当BarArea继承自ChartSeriesBuilderBase时,我们可以声明以下变量:

ChartSeriesBuilderBase<IChartSeries, Bar> myBarBuilder;
ChartSeriesBuilderBase<IChartSeries, Area> myAreaBuilder;

但同样,由于这些变量被输入BarArea,因此它们无法帮助我们创建能够包含两种对象类型的变量。

现在考虑一下类似但简化的情况:

public class MyGeneric<T>
{
}

void q49156521()
{
    var myObject = new MyGeneric<string>();
    var myOtherObject = new MyGeneric<int>();
}

虽然两个变量都是MyGeneric,但我没有可用于定义变量的基类,它可以同时包含myObjectmyOtherObject,因为它们的区别在于通用类型。

请注意,许多泛型类允许您通过实现非泛型接口来解决此确切问题。例如,List<T>实施IList,因此可以使用IList变量同时包含List<string>List<int>。但是,看起来ChartSeriesBuilderBase没有可以使用的界面。

所以我担心看起来答案是,你不能。

我不得不在这里引用另一个问题甚至知道这是一件事:https://stackoverflow.com/a/25166761/8915494

希望这有帮助