#c# #oop #design-patterns
#c# #ооп #шаблоны проектирования
Вопрос:
У меня реализована эта функция для анализа сведений о сотрудниках, аналогично мне придется анализировать данные о продажах, клиентах и т. Д. Для этого мне нужно создать еще 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;
}
Ответ №1:
Я думаю, что шаблон посетителя может быть здесь подходящим. Вы изменяете указанную выше функцию, чтобы включить вызываемый параметр visitor
. Затем вы изменяете свой цикл for для передачи соответствующих данных объекту visitor:
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();
Ответ №2:
Вы могли бы предоставить это из общего класса, в соответствии с:
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()
вызовы передаются на передаваемый лямбда-код, что делает его немного тяжеловесным. Можете ли вы рассказать нам больше о различиях в том, какие ячейки используются между Employee и другими типами?
Комментарии:
1. В таблице excell может быть так много, что я не собираюсь загружать. Например, для таблицы сотрудника я могу загрузить только имя, фамилию и идентификатор emp, иногда я могу загрузить всю таблицу. В таблице customer я мог бы загрузить только идентификатор клиента. Это довольно динамично . Я просто хочу передать параметры интересующих меня столбцов, и это должно работать автоматически.
Ответ №3:
Я переделал свой код примерно так
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;
}
итак, теперь я могу повторно использовать синтаксический анализатор в любых местах, которые я хочу использовать. пожалуйста, прокомментируйте, правильно ли это