通用解析器设计
本文关键字: | 更新日期: 2023-09-27 18:13:30
我有这个函数实现解析员工详细信息,同样我将不得不解析销售,客户等,我需要创建2个以上的函数。代码将在所有函数中重复,唯一的区别是
- 函数的返回类型
- 实例化合适的对象
- 单元格读取
是否有任何方法将重复代码移动到一个类并配置它,以便我可以重用它?
public List<Employee> ParseEmployee(string filePath)
{
Application _excelApp = null;
Workbooks workBooks = null;
Workbook workBook = null;
Sheets wSheets = null;
Worksheet wSheet = null;
Range xlRange = null;
Range xlRowRange = null;
Range xlcolRange = null;
List<Employee> empLst= new List<Employee>();
try
{
_excelApp = new Application();
workBooks = _excelApp.Workbooks;
workBook = workBooks.Open(filePath, Type.Missing, Type.Missing, Type.Missing, Type.Missing,
Type.Missing, Type.Missing, Type.Missing, Type.Missing,
Type.Missing, Type.Missing, Type.Missing, Type.Missing,
Type.Missing, Type.Missing);
wSheets = (Sheets)workBook.Sheets;
wSheet = (Worksheet)wSheets.get_Item(1);
xlRange = wSheet.UsedRange;
xlRowRange = xlRange.Rows;
xlcolRange = xlRange.Columns;
int rowCount = xlRowRange.Count;
int colCount = xlcolRange.Count;
for (int i = 2; i <= rowCount; i++)
{
Range cell1 = xlRange.Cells[i, 1] as Range;
Range cell2 = xlRange.Cells[i, 2] as Range;
Range cell3 = xlRange.Cells[i, 3] as Range;
object val1 = cell1.Value2;
object val2 = cell2.Value2;
object val3 = cell3.Value2;
Employee emp = new Employee();
emp.FirstName = val1.ToString();
emp.LastName = val2.ToString();
emp.EmpID = val3.ToString();
empLst.Add(emp);
Marshal.ReleaseComObject(cell1);
Marshal.ReleaseComObject(cell2);
Marshal.ReleaseComObject(cell3);
}
}
catch (Exception exp)
{
}
finally
{
GC.Collect();
GC.WaitForPendingFinalizers();
workBook.Close(false, Type.Missing, Type.Missing);
_excelApp.Quit();
Marshal.ReleaseComObject(xlRowRange);
Marshal.ReleaseComObject(xlRange);
Marshal.ReleaseComObject(xlcolRange);
Marshal.ReleaseComObject(wSheet);
Marshal.ReleaseComObject(wSheets);
Marshal.ReleaseComObject(workBook);
Marshal.ReleaseComObject(workBooks);
Marshal.ReleaseComObject(_excelApp);
}
return empLst;
}
我认为访问者模式可能很适合这里。您可以修改上面的函数,使其包含一个名为visitor
的参数。然后修改for循环,将相关数据传递给访问者对象:
for (int i = 2; i <= rowCount; i++)
{
visitor.VisitRow(xlRange.Cells, i);
}
visitor.VisitRow()
函数将提取它需要的数据,并在内部保持对提取对象的引用。你会有不同的访客,一个是雇主,一个是销售,客户等。
最后,您将编写如下内容:
Visitor employerVisitor = new EmployerVisitor();
Visitor salesVisitor = new SalesVisitor();
Parse("workbook-employers.xls", employerVisitor);
Parse("workbook-sales.xls", salesVisitor);
List<Employee> employers = employerVisitor.GetData();
List<Sale> sales = salesVisitor.GetData();
您可以从泛型类中公开它,如下所示:
public class ObjectParser<T>
{
public List<T> ParseObject(string filePath, Func<Range, T> f)
{
Application _excelApp = null;
Workbooks workBooks = null;
Workbook workBook = null;
Sheets wSheets = null;
Worksheet wSheet = null;
Range xlRange = null;
Range xlRowRange = null;
Range xlcolRange = null;
List<T> lst= new List<T>();
try
{
_excelApp = new Application();
workBooks = _excelApp.Workbooks;
workBook = workBooks.Open(filePath, Type.Missing, Type.Missing, Type.Missing, Type.Missing,
Type.Missing, Type.Missing, Type.Missing, Type.Missing,
Type.Missing, Type.Missing, Type.Missing, Type.Missing,
Type.Missing, Type.Missing);
wSheets = (Sheets)workBook.Sheets;
wSheet = (Worksheet)wSheets.get_Item(1);
xlRange = wSheet.UsedRange;
xlRowRange = xlRange.Rows;
xlcolRange = xlRange.Columns;
int rowCount = xlRowRange.Count;
int colCount = xlcolRange.Count;
for (int i = 2; i <= rowCount; i++)
{
lst.Add(f(xlRange));
}
}
catch (Exception exp)
{
}
finally
{
GC.Collect();
GC.WaitForPendingFinalizers();
workBook.Close(false, Type.Missing, Type.Missing);
_excelApp.Quit();
Marshal.ReleaseComObject(xlRowRange);
Marshal.ReleaseComObject(xlRange);
Marshal.ReleaseComObject(xlcolRange);
Marshal.ReleaseComObject(wSheet);
Marshal.ReleaseComObject(wSheets);
Marshal.ReleaseComObject(workBook);
Marshal.ReleaseComObject(workBooks);
Marshal.ReleaseComObject(_excelApp);
}
return lst;
}
}
使用:
ObjectParser<Employee> op = new ObjectParser<Employee>()
op.Parse(filepath, r => /* insert code to handle Employee here */)
我这里担心的是一些Marshall.ReleaseComObject()
调用被推到传入的lambda上,这使得它有点重。您能告诉我们更多关于Employee和其他类型之间使用的单元格的差异吗?
我已经将我的代码重构为如下内容
class ExcelParser : IDisposable
{
bool disposed = false;
Application _excelApp = null;
Workbooks workBooks = null;
Workbook workBook = null;
Sheets wSheets = null;
Worksheet wSheet = null;
Range xlRange = null;
Range xlRowRange = null;
Range xlcolRange = null;
public bool Load(string filePath)
{
bool bFlag = true;
try
{
_excelApp = new Application();
workBooks = _excelApp.Workbooks;
workBook = workBooks.Open(filePath, Type.Missing, Type.Missing, Type.Missing, Type.Missing,
Type.Missing, Type.Missing, Type.Missing, Type.Missing,
Type.Missing, Type.Missing, Type.Missing, Type.Missing,
Type.Missing, Type.Missing);
wSheets = (Sheets)workBook.Sheets;
wSheet = (Worksheet)wSheets.get_Item(1);
xlRange = wSheet.UsedRange;
xlRowRange = xlRange.Rows;
xlcolRange = xlRange.Columns;
}
catch (Exception exp)
{
throw;
}
return bFlag;
}
public int GetRowCount()
{
int rowCount = 0;
if(xlRowRange != null)
rowCount = xlRowRange.Count;
return rowCount;
}
public string GetValue(int rowIndex, int colIndex)
{
string value = "";
Range cell = null;
try
{
cell = xlRange.Cells[rowIndex, colIndex] as Range;
object val = cell.Value2;
value = val.ToString();
}
catch (Exception exp)
{
}
finally
{
Marshal.ReleaseComObject(cell);
}
return value;
}
protected virtual void Dispose(bool disposing)
{
if (!this.disposed)
{ // don't dispose more than once
if (disposing)
{
// disposing==true means you're not in the finalizer, so
// you can reference other objects here
GC.Collect();
GC.WaitForPendingFinalizers();
if (workBook != null)
workBook.Close(false, Type.Missing, Type.Missing);
if (_excelApp != null)
_excelApp.Quit();
if (xlRowRange != null)
Marshal.ReleaseComObject(xlRowRange);
if (xlRange != null)
Marshal.ReleaseComObject(xlRange);
if (xlcolRange != null)
Marshal.ReleaseComObject(xlcolRange);
if (wSheet != null)
Marshal.ReleaseComObject(wSheet);
if (wSheets != null)
Marshal.ReleaseComObject(wSheets);
if (workBook != null)
Marshal.ReleaseComObject(workBook);
if (workBooks != null)
Marshal.ReleaseComObject(workBooks);
if (_excelApp != null)
Marshal.ReleaseComObject(_excelApp);
}
}
this.disposed = true;
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
~ExcelParser()
{
Dispose(false);
}
}
和调用代码看起来像这样
public List<Employee> Handle(string filePath)
{
List<Employee> empLst = new List<Employee>();
ExcelParser exlParser = new ExcelParser();
try
{
if (exlParser.Load(filePath))
{
int rowCount = exlParser.GetRowCount();
for (int i = 2; i <= rowCount; i++)
{
Employee emp = new Employee();
emp.FirstName = exlParser.GetValue(i, 1);
emp.LastName = exlParser.GetValue(i, 2);
emp.EmpID = exlParser.GetValue(i, 3);
empLst.Add(emp);
}
}
}
catch (Exception exp)
{
}
finally
{
exlParser.Dispose();
}
return empLst;
}
所以现在我可以在任何我想使用的地方重用解析器。请评论这是否正确