使用接口传递通用对象?

时间:2017-10-03 15:40:47

标签: c# generics

我正在构建一个工具来简单地导出到excel以供以后重复使用。我遇到的一个问题是传递包含我的单元格数据的对象的简单而可靠的方法。

包含我的单元格数据的类如下所示:

public interface IExcelCell { 

}

public class ExcelCell<T> : IExcelCell
{
    public T Value { get; set; }
}

我正在使用它:

private void GenerateCell(IExcelCell cell, int currentColumn)
{
    // check type of Value, apply optional formatting
}

基本上我需要做的是检查传入数据是否属于某种数据类型,例如string,double,DateTime。有没有方便的方法呢?

修改

这是相关的出口代码,用于说明我正在尝试做的事情:

public class ExcelExporter
{
    private ExcelPackage excelPackage;
    private OfficeOpenXml.ExcelWorksheet currentWorksheet;
    private int currentRow;

    public ExcelPackage ExportExcelWorkbook(ExcelWorkbook workbook)
    {
        excelPackage = new ExcelPackage();
        GenerateWorkbook(workbook);

        return excelPackage;
    }

    private void GenerateWorkbook(ExcelWorkbook workbook)
    {
        foreach (var worksheet in workbook.Worksheets)
        {
            GenerateWorksheet(worksheet);
        }
    }

    private void GenerateWorksheet(ExcelWorksheet worksheet)
    {
        currentWorksheet = excelPackage.Workbook.Worksheets.Add(worksheet.Name);
        currentRow = 1;

        foreach (var section in worksheet.Sections)
        {
            GenerateSection(section);
        }

    }

    private void GenerateSection(ExcelSection section)
    {
        if (!section.Name.IsNullOrEmpty())
        {
            currentWorksheet.Cells[currentRow, 1].Value = section.Name;
            currentRow++;
        }

        if (section.Headers != null && section.Headers.Any())
        {
            GenerateHeaders(section.Headers);
            currentRow++;
        }

        foreach (var row in section.Rows)
        {
            GenerateRow(row);
            currentRow++;
        }
    }

    private void GenerateHeaders(IEnumerable<string> headers)
    {
        var enumerable = headers as IList<string> ?? headers.ToList();

        for (var i = 0; i < enumerable.Count(); i++)
        {
            currentWorksheet.Cells[currentRow, i + 1].Value = enumerable[i];
        }
    }

    private void GenerateRow(ExcelRow row)
    {
        var currentColumn = 0;

        foreach (var cell in row.RowData)
        {
            GenerateCell(cell, currentColumn);
            currentColumn++;
        }
    }

    private void GenerateCell(IExcelCell cell, int currentColumn)
    {
        var excelCell = (ExcelCell<object>) cell;

        if (excelCell.Value is DateTime)
        {
            currentWorksheet.Cells[currentRow, currentColumn].Style.Numberformat.Format = "mm/dd/yyyy";
        }

        currentWorksheet.Cells[currentRow, currentColumn].Value = excelCell.Value;
    }
}

修改2

根据Gilad的回答,我提出了一个稍微不同的解决方案:

// interface
public interface IExcelCell { 
    object Value { get; set; }
    string NumberFormat { get; }
}

// implementation
public class ExcelCell : IExcelCell
{
    public object Value { get; set; }

    public string NumberFormat => null;
}

public class DateTimeExcelCell : IExcelCell
{
    public object Value { get; set; }

    public string NumberFormat => "mm/dd/yyyy";
}

// Cell generation
private void GenerateCell(IExcelCell cell, int currentColumn)
{
    if (cell.NumberFormat != null)
    {
        currentWorksheet.Cells[currentRow, currentColumn].Style.Numberformat.Format = cell.NumberFormat;
    }

    currentWorksheet.Cells[currentRow, currentColumn].Value = cell.Value;
}

1 个答案:

答案 0 :(得分:3)

查看更新的问题:

正如我所建议的那样,使用is上的cell运算符:

if (cell is ExcelCell<DateTime>)
{
    currentWorksheet.Cells[currentRow, currentColumn].Style.Numberformat.Format = "mm/dd/yyyy";
}

但仍然可以在下面看到有关工厂模式部分的答案

原始回答:

我想最干净的是将接口声明为通用接口并且其中包含Value。如果没有,那么您可以使用is运算符。例如:

IExcelCell cell = new ExcelCell<DateTime>();
bool result1 = cell is ExcelCell<DateTime>; // true
bool result2 = cell is ExcelCell<int>; // false

所以在你的功能中:

private void GenerateCell(IExcelCell cell, int currentColumn)
{
    if(cell is ExcelCell<DateTime>)
    {
    }
}

如果你想使用它,可以使用as运算符:

private void GenerateCell(IExcelCell cell, int currentColumn)
{
    var c = cell as ExcelCell<DateTime>
    if(c != null)
    {
        // TODO - use casted c
    }
}

从您的评论中,似乎知道类型的目的是执行格式化。我建议使用一个Factory模式,给出特定类型的特定类型格式。或者,如果您更改界面,请执行以下操作:

public interface IExcelCell<T> 
{
    T Value { get; set; }
}

public class DateTimeExcelCell : IExcelCell<DateTime>
{
    public DateTime Value { get; set; }

    public override string ToString()
    {
        // TODO - formatting you want for DateTime
    }
}