当涉及 "recursive" 泛型时,如何声明通用基类型?

How can I declare a common base type when "recursive" generics are involved?

我正在使用大量使用泛型的 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对象是我们自己的class,其中包含我们要创建的系列的配置信息.

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

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() 的 return 类型开始的 class 层次结构。

// 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>
{
}

tl;dr 除非 ChartSeriesBuilderBase 实现其他一些 non-generic 接口。


您可以创建一个继承自 ChartSeriesBuilderBase 的 class,然后实例化它,这样就可以了,如下所示:

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

这有点令人困惑,但由于 MyBuilder 正在定义,它能够将自身引用为其基础 class.

的泛型类型

我想 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,但没有基数 class 可以用来定义一个可以同时包含 myObjectmyOtherObject 的变量] 因为它们的泛型不同。

请注意,许多通用 classes 允许您通过实现 non-generic 接口来解决这个确切的问题。例如,List<T> 实现了 IList,因此可以使用 IList 变量来包含 List<string>List<int>。但是,ChartSeriesBuilderBase好像没有可以使用的接口。

所以恐怕答案是,你不能。

我不得不在 SO 上引用另一个问题,甚至知道这是一回事:

希望对您有所帮助