我的架构设置很大程度上依赖于泛型。有一点,我必须投射到基本类型,并且出于好奇心,我想知道我是否可以回避它。
背景是我们有许多生产线组要打印到网页上。由于每个组的生产线都有一些相同的信息,但也有一些不同的信息,我使用了一个抽象类:
public abstract class ProductionLine
{
public int LineNumber { get; set; }
public string Name { get; set; }
public double Target { get; set; }
public double Actual { get; set; }
public double Variance { get; set; }
}
以下是具体实施示例:
public class TissueProductionLine : ProductionLine
{
public double Budget { get; set; }
public double PercentOnTarget { get; set; }
}
由于我们讨论的是生产线组,我创建了一个抽象对象来存放该组及其所有生产线的名称:
public abstract class ProductionLineGroup<T> where T : ProductionLine
{
public string Name { get; set; }
public List<T> ProductionLines { get; set; }
}
这是一个小组的具体实现:
public class TissueProductionLineGroup : ProductionLineGroup<TissueProductionLine>
{
public string TissueType { get; set; }
}
一切都很好,但我希望能够将生产线组作为一系列表格呈现给HTML。为了区分我的顾虑,我创建了将使用组并发出所需标记的格式化程序。使用工厂,我将选择适当的格式化程序:
public static class ProductionLineGroupFormatterFactory<T> where T : ProductionLine
{
public static ProductionLineGroupFormatter<T> GetProductionLineGroupFormatter(ProductionLineGroup<T> group)
{
if (typeof(T) == typeof(TissueProductionLineGroup))
{
return new TissueProductionLineGroupFormatter<T>();
}
throw new ApplicationException("Could not find an appropriate formatter for this Production Line type:" + typeof(T).ToString());
}
}
factory方法返回一个基类型为ProductionLineGroupFormatter的对象:
public abstract class ProductionLineGroupFormatter<T> where T : ProductionLine
{
public abstract string Render(ProductionLineGroup<T> group);
}
实际进行渲染的派生类型是TissueProductionLineGroupFormatter:
public class TissueProductionLineGroupFormatter<T> : ProductionLineGroupFormatter<T> where T : ProductionLine
{
public override string Render(ProductionLineGroup<T> group)
{
foreach (ProductionLine line in group.ProductionLines)
{
TissueProductionLine tLine = (TissueProductionLine)line;
sb.Append(@"<tr>
<td>" + tLine.Name + @"</td>
<td>" + tLine.Actual + @"</td>
<td>" + tLine.Target + @"</td>
<td>" + tLine.Variance + @"</td>
<td>" + tLine.PercentOnTarget + @"</td>
</tr>");
}
}
return sb.ToString();
}
}
注意我需要在foreach循环中进行的演员表。我无法弄清楚是否可以避免它。我最初试图将T限制为TissueProductionLine,因为它是从ProductionLine派生的,但是我得到一个错误“没有从'T'到'TissueProductionLine'的隐式引用转换。然后我尝试创建一个隐式运算符,以便转换可以是在ProductionLine和TissueProductionLine之间制作,但你不能用基础和派生类型做到这一点。
如果有人能说清楚这一点,我会很高兴我的好奇心得到满足!谢谢!
克里斯
答案 0 :(得分:3)
你应该可以这样做:
public class TissueProductionLineGroupFormatter :
ProductionLineGroupFormatter<TissueProductionLine> {
...
}
<强>更新强>
对于工厂,您可以实现特定于类型的方法,而不是使用条件逻辑来测试类型:
public static class ProductionLineGroupFormatterFactory {
public static ProductionLineGroupFormatter<TissueProductionLine> GetProductionLineGroupFormatter(ProductionLineGroup<TissueProductionLine> group) {
return new TissueProductionLineGroupFormatter();
}
// .. other factory methods....
}
也许你可以创建更好的命名方法,这样就不需要参数:
public static class ProductionLineGroupFormatterFactory {
public static ProductionLineGroupFormatter<TissueProductionLine> GetTissueProductionLineGroupFormatter() {
return new TissueProductionLineGroupFormatter();
}
// .. other factory methods....
}
答案 1 :(得分:1)
如果您让您的类实现此接口,并让您的工厂返回IProductionLineGroupFormatter<T>
,这应该可以。
public interface IProductionLineGroupFormatter<out T> where T : ProductionLine
{
public string Name { get; set; }
public IEnumerable<T> ProductionLines { get; set; }
}
和Jordão的解决方案一样:
public class TissueProductionLineGroupFormatter :
ProductionLineGroupFormatter<TissueProductionLine>, IProductionLineGroupFormatter<TissueProductionLine> {
...
}
答案 2 :(得分:0)
我这样做:
public abstract class ProductionLineGroupFormatter<T> where T : ProductionLine
{
public string Render(ProductionLineGroup<T> group)
{
foreach (T line in group.ProductionLines)
{
AppendProductionLine(line);
}
return sb.ToString();
}
protected abstract void AppendProductionLine(T line);
}
public class TissueProductionLineGroupFormatter : ProductionLineGroupFormatter<TissueProductionLine>
{
protected override void AppendProductionLine(TissueProductionLine line)
{
sb.Append(@"<tr>
<td>" + line.Name + @"</td>
<td>" + line.Actual + @"</td>
<td>" + line.Target + @"</td>
<td>" + line.Variance + @"</td>
<td>" + line.PercentOnTarget + @"</td>
</tr>");
}
}