只能包含图像或字符串的 C# 列表
C# List which could contain only image or string
NET 开发人员。
决定制作图层以从数据源中提取一些数据,以进一步在一些生成的文档中构建 tables。
也许问题很简单,但我有点困惑。
问题是 - 想要构建 table 可能包含字符串或图像的模型。
目前我得到了那组 类,但我不确定这是正确的方法:
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]);
}
}
}
更新:
数据类型 - 可以是简单的字符串或 jpg/png/bmp 图片
UPD2:
Txnks 伙计们,意识到我不需要 GetElement
功能,这里没用。
您的扩展方法 DataUnitModelExtensions.GetElement
所做的唯一一件事就是确保 DataUnitModel.Element
中包含的数据是 Image
、string
或 null
.从您的问题来看,这是此 属性 可以 包含的唯一数据类型。所以你的扩展方法基本上没有任何用处。您在那里使用了两个非常适合类型转换的 as
强制转换,但是您 return 一个 object
这使得您的类型转换毫无用处。
如果要公开 Element
字段的内容,请使用 return Image
和 string
两个不同的属性。由于您自己定义 DataUnitModel
class,因此不需要扩展方法:
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; } }
}
您可以使用接口:
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());
并且在检索时您可以检查对象的类型。您也只能输入图片和字符串。
如果 table 只能包含图像或字符串,您可以使用泛型。
看来你在这里采取了一个稍微错误的方法,即你正在解决错误的问题。你的 "table" 可以简单地是一个 List<List<object>>
,它基本上携带与你的 类 现在携带的相同的信息(除了 "number of columns",如果列表是空的) .
如果我理解你的问题,每种元素类型都将以某种方式 "rendered"(作为报告,或在网页中,或转换为其他形式)。如果那是真的,我将集中精力定义所有元素的渲染步骤的共同点。因此,最终结果应该允许您编写如下内容:
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);
}
简化的报告模型可以是段落列表、tables、数字,例如:
<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
类型(项目类型 -> 渲染器)创建渲染器的 映射。这甚至可以通过 reflection/dependency 注入来完成,因此当您决定创建新的渲染器类型时,一切都会 "just work":
// 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);
}
这会将您带到 table 个项目,这些项目对您来说很有趣,因为它们显然可以包含图像和段落作为 table 个单元格。这意味着您可以像这样定义它们:
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;
NET 开发人员。 决定制作图层以从数据源中提取一些数据,以进一步在一些生成的文档中构建 tables。
也许问题很简单,但我有点困惑。
问题是 - 想要构建 table 可能包含字符串或图像的模型。 目前我得到了那组 类,但我不确定这是正确的方法:
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]);
}
}
}
更新:
数据类型 - 可以是简单的字符串或 jpg/png/bmp 图片
UPD2:
Txnks 伙计们,意识到我不需要 GetElement
功能,这里没用。
您的扩展方法 DataUnitModelExtensions.GetElement
所做的唯一一件事就是确保 DataUnitModel.Element
中包含的数据是 Image
、string
或 null
.从您的问题来看,这是此 属性 可以 包含的唯一数据类型。所以你的扩展方法基本上没有任何用处。您在那里使用了两个非常适合类型转换的 as
强制转换,但是您 return 一个 object
这使得您的类型转换毫无用处。
如果要公开 Element
字段的内容,请使用 return Image
和 string
两个不同的属性。由于您自己定义 DataUnitModel
class,因此不需要扩展方法:
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; } }
}
您可以使用接口:
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());
并且在检索时您可以检查对象的类型。您也只能输入图片和字符串。
如果 table 只能包含图像或字符串,您可以使用泛型。
看来你在这里采取了一个稍微错误的方法,即你正在解决错误的问题。你的 "table" 可以简单地是一个 List<List<object>>
,它基本上携带与你的 类 现在携带的相同的信息(除了 "number of columns",如果列表是空的) .
如果我理解你的问题,每种元素类型都将以某种方式 "rendered"(作为报告,或在网页中,或转换为其他形式)。如果那是真的,我将集中精力定义所有元素的渲染步骤的共同点。因此,最终结果应该允许您编写如下内容:
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);
}
简化的报告模型可以是段落列表、tables、数字,例如:
<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
类型(项目类型 -> 渲染器)创建渲染器的 映射。这甚至可以通过 reflection/dependency 注入来完成,因此当您决定创建新的渲染器类型时,一切都会 "just work":
// 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);
}
这会将您带到 table 个项目,这些项目对您来说很有趣,因为它们显然可以包含图像和段落作为 table 个单元格。这意味着您可以像这样定义它们:
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;