试图避免使用我的泛型

时间:2012-09-06 18:14:29

标签: c# .net generics

我的架构设置很大程度上依赖于泛型。有一点,我必须投射到基本类型,并且出于好奇心,我想知道我是否可以回避它。

背景是我们有许多生产线组要打印到网页上。由于每个组的生产线都有一些相同的信息,但也有一些不同的信息,我使用了一个抽象类:

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之间制作,但你不能用基础和派生类型做到这一点。

如果有人能说清楚这一点,我会很高兴我的好奇心得到满足!谢谢!

克里斯

3 个答案:

答案 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>"); 
    } 
}