Общий дизайн синтаксического анализатора

#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;
        }
  

итак, теперь я могу повторно использовать синтаксический анализатор в любых местах, которые я хочу использовать. пожалуйста, прокомментируйте, правильно ли это