当涉及 "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.
的泛型类型
我想 Bar
和 Area
也以类似的方式定义,因此在它们自己的通用定义中引用它们自己。由于Bar
和Area
继承自ChartSeriesBuilderBase
,我们可以声明如下变量:
ChartSeriesBuilderBase<IChartSeries, Bar> myBarBuilder;
ChartSeriesBuilderBase<IChartSeries, Area> myAreaBuilder;
但同样,由于这些变量的类型为 Bar
或 Area
,它们无法帮助我们创建能够包含两种对象类型的变量。
现在考虑这个类似但简化的情况:
public class MyGeneric<T>
{
}
void q49156521()
{
var myObject = new MyGeneric<string>();
var myOtherObject = new MyGeneric<int>();
}
虽然两个变量都是 MyGeneric
,但没有基数 class 可以用来定义一个可以同时包含 myObject
和 myOtherObject
的变量] 因为它们的泛型不同。
请注意,许多通用 classes 允许您通过实现 non-generic 接口来解决这个确切的问题。例如,List<T>
实现了 IList
,因此可以使用 IList
变量来包含 List<string>
和 List<int>
。但是,ChartSeriesBuilderBase
好像没有可以使用的接口。
所以恐怕答案是,你不能。
我不得不在 SO 上引用另一个问题,甚至知道这是一回事:
希望对您有所帮助
我正在使用大量使用泛型的 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.
我想 Bar
和 Area
也以类似的方式定义,因此在它们自己的通用定义中引用它们自己。由于Bar
和Area
继承自ChartSeriesBuilderBase
,我们可以声明如下变量:
ChartSeriesBuilderBase<IChartSeries, Bar> myBarBuilder;
ChartSeriesBuilderBase<IChartSeries, Area> myAreaBuilder;
但同样,由于这些变量的类型为 Bar
或 Area
,它们无法帮助我们创建能够包含两种对象类型的变量。
现在考虑这个类似但简化的情况:
public class MyGeneric<T>
{
}
void q49156521()
{
var myObject = new MyGeneric<string>();
var myOtherObject = new MyGeneric<int>();
}
虽然两个变量都是 MyGeneric
,但没有基数 class 可以用来定义一个可以同时包含 myObject
和 myOtherObject
的变量] 因为它们的泛型不同。
请注意,许多通用 classes 允许您通过实现 non-generic 接口来解决这个确切的问题。例如,List<T>
实现了 IList
,因此可以使用 IList
变量来包含 List<string>
和 List<int>
。但是,ChartSeriesBuilderBase
好像没有可以使用的接口。
所以恐怕答案是,你不能。
我不得不在 SO 上引用另一个问题,甚至知道这是一回事:
希望对您有所帮助