C#列表,只能包含图像或字符串

时间:2016-12-21 07:52:55

标签: c# oop data-conversion

NET开发人员。 决定使图层从数据源中提取一些数据,以进一步在某些生成的文档中构建表。

也许问题很简单,但我有点困惑。

问题是 - 想要构建可能包含字符串或图像的表模型。 目前我最终得到了这组课程,但我不确定这是否正确:

public class TableModel
{
    public long RowsCount { get; set; }

    public long ColumnsCount { get; set; }

    public List<List<DataUnitModel>> Table { get; set; } 
}

public class DataUnitModel
{
    public object Element { get; set; }

    public Type ElementType { get; set; }
}

它假设是这样的(伪代码):

public void BuildTable(TableModel myTable)
{
    for (var i = 0; i < myTable.Rows)
    {
        for (var j = 0; j < myTable.Cols)
        {
            DocumentCurrentRange.Insert(myTable.Table[i][j]);
        }
    }
}     

UPD:

数据类型 - 它可以是jsut简单字符串或jpg / png / bmp图像

UPD2:

Txnks家伙,意识到我不需要GetElement功能,这里没用。

3 个答案:

答案 0 :(得分:1)

您的扩展程序DataUnitModelExtensions.GetElement唯一能做的就是确保DataUnitModel.Element中包含的数据是Imagestringnull。从您的问题来看,这是此属性可以包含的唯一数据类型。所以你的扩展方法基本上没有用。你在那里使用了两个as强制转换,非常适合类型转换,但是你返回一个object,这使得你的类型转换没用。

如果要公开Element字段的内容,请使用返回Imagestring的两个不同属性。由于您自己定义DataUnitModel类,因此不需要扩展方法:

public class DataUnitModel
{
    public object Element { get; set; }

    public string ElementText { get { return Element as string; } }

    public Image ElementImage { get { return Element as Image; } }

    public Type ElementType { get; set; }
}

如果您想获取元素类型,可以使用GetType作为ElementType属性,尽管一些简单的bool属性可以完成此任务:

public class DataUnitModel
{
    public object Element { get; set; }

    public string ElementText { get { return Element as string; } }

    public Image ElementImage { get { return Element as Image; } }

    public Type ElementType { get { Element != null ? Element.GetType() : null; }

    public bool IsElementText { get { return Element is string; } }

    public bool IsElementImage { get { return Element is Image; } }
}

答案 1 :(得分:1)

您可以使用界面:

public interface IDataUnitModel{}
public class StringDataUnitModel : IDataUnitModel
{
  public string Value {get;set;}
}
public class ImageDataUnitModel : IDataUnitModel
{
  public Image Value {get;set;}
}
//...
public List<IDataUnitModel> list = new List<IDataUnitModel>();
list.Add(new StringDataUnitModel{Value = "ABC"});
list.Add(new ImageDataUnitModel());

在检索时,您可以检查对象的类型。你也只能放入图像&amp;字符串。

如果表格只能包含图片或字符串,则可以使用泛型。

答案 2 :(得分:1)

看来你在这里采取了一种稍微错误的做法,即你正在解决错误的问题。你的“表格”可能只是一个List<List<object>>,它基本上会带有你班上现在携带的相同信息(除了“列数”,如果列表为空)。

如果我理解了您的问题,每种元素类型将以某种方式“呈现”(作为报告,或在网页中,或转换为其他内容)。如果这是真的,我将集中精力定义所有元素的渲染步骤的共同点。因此,最终结果应该允许您编写类似的内容:

var html = new StringBuilder();

foreach (var item in document.Items)
{
    // get the renderer for this item type
    var renderer = renderers[item.GetType()];

    // append to the report
    renderer.Append(html, item);
}

简化的报告模型可以是段落,表格,图形的列表,例如:

<Document>

  <Paragraph>
     Some text
  </Paragraph>

  <Table>
     <HeaderRow>
        <Col>Num</Col> <Col>Name</Col> ...
     <HeaderRow>
     <Row>
        <Col>Num</Col> <Col>Name</Col> ...
     <Row>
  </Table>

  <Image Url="..." />

</Document>

或许每个元素也都有一个子元素列表,但这是一个简单的例子。

然后您的模型将用以下内容表达:

interface IDocument 
{
    List<IItem> Items { get; }
}

interface IItem
{
    // common properties which apply to all items
    // (if they don't apply to all items, they
    // shouldn't be here)

    bool CanBreakAcrossPages { get; }
}

interface ITextualItem : IItem
{
    string TextFont { get; }
    float TextSize { get; }
    ...
}

class ParagraphItem : ITextualItem
{
    public bool CanBreakAcrossPages { get; set; }
    public string TextFont { get; set; }
    public float TextSize { get; set; }
    string Text { get; set; }
}

... you get the idea

然后,独立地,你可能有这样的渲染器界面:

// there are several ways to do this, but this is
// just a simple way to avoid casting all over the 
// code, and still have generics for better type safety

interface IHtmlRenderer
{
    void Append(StringBuilder html, object element);
}

interface IHtmlRenderer<T> : IHtmlRenderer where T : IItem
{
    void Append(StringBuilder html, T element);
}

abstract class BaseHtmlRenderer<T> : IHtmlRenderer<T> where T : IItem
{
    public void Append(StringBuilder html, object element)
    {
        // this is the only place where we will cast
        this.Append(html, (T)element);
    }

    abstract public void Append(StringBuilder html, T element);
}

这使得创建不同的渲染器变得明显:

class ParagraphRenderer : BaseHtmlRenderer<ParagraphItem>
{
    public override void Append(StringBuilder html, ParagraphItem element)
    {
        // do stuff
    }
}

class ImageRenderer : BaseHtmlRenderer<ImageItem>
{
    public override void Append(StringBuilder html, ImageItem element)
    {
        // do stuff
    }
}

您要做的就是为每个IItem类型(项目类型 - &gt;渲染器)创建映射渲染器。这甚至可以通过反射/依赖注入来完成,这样当你决定创建一个新的渲染器类型时,一切都会“正常工作”:

// this can easily be done automatically through reflection

var renderers = new Dictionary<Type, Action<StringBuilder, object>>();
renderers[typeof(ImageItem)] = new ParagraphRenderer().Append;
renderers[typeof(ParagraphItem)] = new ParagraphRenderer().Append;

现在我们达到了我们想要的用途:

var html = new StringBuilder();

foreach (var item in document.Items)
{
    // get the renderer for this item type
    var renderer = renderers[item.GetType()];

    // append to the report
    renderer.Append(html, item);
}

这会将您带到表格项目,这些表项目在您的案例中很有趣,因为它们显然可以包含图像和段落作为表格单元格。这意味着您可以将它们定义为:

class TableItem : ITextualItem
{
    // note the recursion here: 
    // ITableItem is an IItem, and it contains a list of IItems,
    // meaning you can even have nested tables inside a single table cell    

    public List<IItem> HeaderCols { get; }
    public List<List<IItem>> Rows { get; }
}

然后渲染器再次有趣,因为它是一个复合(递归)渲染器:

public class TableRenderer : BaseHtmlRenderer<TableItem>
{
    // we need to be able to render individual cells
    private Dictionary<Type, Action<StringBuilder, object>> _renderers;

    public override void Append(StringBuilder html, TableItem element)
    {
        RenderHeaderRowStart(html);        
        foreach (var col in element.HeaderCols)
        {
            var cellRenderer = _renderers[col.GetType()];
            cellRenderer.Append(html);
        }
        RenderHeaderRowEnd(html);

        ...
    } 
}

这意味着您需要将字典传递给渲染器构造函数:

// renderers dictionary contains a TableRenderer, which also keeps
// a reference to this same dictionary
renderers[typeof(TableItem)] = new TableRenderer(renderers).Append;