Какую архитектуру использовать для устранения этой исключительной ситуации SystemOutOfMemoryException, позволяя мне создавать экземпляры ячеек листа? - PullRequest
7 голосов
/ 03 марта 2011

Резюме

Этот вопрос является продолжением желания создать простой API для электронных таблиц, оставляя его удобным для тех, кто хорошо знает Excel.

Подводя итог, этот вопрос относится к следующим двум:
1. Как реализовать самостоятельное именование столбца из его индекса? ;
2. Как ускорить инициализацию этой пользовательской таблицы? .

Цель

Предоставление упрощенного API-интерфейса Excel, используемого в качестве оболочки для невралгических компонентов, таких как классы / интерфейсы Application, Workbook, Worksheet и Range, при этом отображаются только наиболее часто используемые свойства объекта для каждого из них.

Пример использования

Этот пример использования основан на модульных тестах, которые позволили мне довести это решение до его нынешнего состояния.

Dim file as String = "C:\Temp\WriteTest.xls"

Using mgr As ISpreadsheetManager = New SpreadsheetManager()
    Dim wb as IWorkbook = mgr.CreateWorkbook()
    wb.Sheets("Sheet1").Cells("A1").Value = 3.1415926
    wb.SaveAs(file)
End Using

А теперь открываем:

Dim file as String = "C:\Temp\WriteTest.xls"

Using mgr As ISpreadsheetManager = New SpreadsheetManager()
    Dim wb as IWorkbook = mgr.OpenWorkbook(file)
    // Working with workbook here...
End Using

Обсуждение

При создании экземпляра книги Excel:

  1. Экземпляр рабочего листа автоматически инициализируется в коллекции Workbook.Sheets;
  2. После инициализации рабочая таблица инициализирует свои ячейки с помощью объекта Range, который может представлять одну или несколько ячеек.

Эти ячейки сразу же становятся доступны со всеми их свойствами, как только рабочая таблица существует.

Мое желание - воспроизвести это поведение так, чтобы

  1. Конструктор класса Workbook инициализирует свойство коллекции Workbook.Sheets с собственными листами;
  2. Конструктор класса Worksheet инициализирует свойство коллекции Worksheet.Cells собственными ячейками.

Моя проблема возникла из-за конструктора класса Worksheet при инициализации свойства коллекции Worksheet.Cells, показанного в # 2.

Мысли

После этих вышеупомянутых вопросов возникли проблемы, я хочу выяснить другую архитектуру, которая позволила бы мне:

  1. Доступ к определенной функции ячейки Range при необходимости;
  2. Доставка наиболее часто используемых свойств через мой ICell интерфейс;
  3. Имеет доступ ко всем Range ячейкам рабочего листа с момента его инициализации.

Принимая во внимание, что доступ к свойству Range.Value - это самое быстрое взаимодействие с базовым экземпляром приложения Excel с использованием Interop.

Итак, я подумал об инициализации моего ReadonlyOnlyDictionary(Of String, ICell) с именем ячеек без немедленного переноса экземпляра интерфейса Range, чтобы я мог просто сгенерировать индексы строк и столбцов вместе с именем ячейки для индексации моего словаря. затем, присваивая свойство Cell.NativeCell, только когда требуется получить доступ или отформатировать определенную ячейку или диапазон ячеек.

Таким образом, данные в словаре будут проиндексированы с именем ячеек, полученных из индексов столбцов, сгенерированных в конструкторе класса Worksheet. Тогда, когда кто-то сделает это:

Using mgr As ISpreadsheetManager = New SpreadsheetManager()
    Dim wb As IWorkbook = mgr.CreateWorkbook()
    wb.Sheet(1).Cells("A1").Value = 3.1415926 // #1:
End Using

# 1: Это позволило бы мне использовать индексы из моего класса Cell для записи заданного значения в конкретную ячейку, что быстрее, чем использование его имени непосредственно против Range.

Вопросы и проблемы

Кроме того, при работе с UsedRange.get_Value() или Cells.get_Value() это возвращает массивы Object (,).

1. Так должен ли я быть доволен работой с Object(,) массивами для ячеек, не имея возможности каким-либо образом его отформатировать?

2. Как спроектировать эти классы Worksheet и Cell, чтобы у меня была лучшая производительность при работе с массивами Object(,), при этом сохраняя возможность того, что экземпляр Cell может представлять или оборачивать диапазон Range одной ячейки?

Спасибо всем, кто нашел время, чтобы прочитать мой пост, и мою искреннюю благодарность тем, кто отвечает.

1 Ответ

0 голосов
/ 16 марта 2011

Используемая архитектура прошла через класс объектов, который я назвал CellCollection.Вот что он делает:

На основании этих гипотез:

  1. Учитывая, что на листе Excel имеется 256 столбцов и 65536 строк;

  2. Учитывая, что за один раз необходимо было создать 16 777 216 (256 * 65536) ячеек;

  3. Учитывая, что наиболее распространенное использование рабочего листа занимаетменее 1000 строк и менее 100 столбцов;

  4. Учитывая, что мне нужно было иметь возможность ссылаться на ячейки с их адресами («A1»);и

  5. С учетом того, что тестируется одновременный доступ ко всем значениям и их загрузка в object[,] в памяти как самый быстрый способ работы с базовой рабочей таблицей Excel., *

Я решил не создавать экземпляры каких-либо ячеек, позволяя моему свойству CellCollection в моем интерфейсе IWorksheet инициализироваться и быть пустым при создании экземпляра, за исключением существующегоУчебное пособие.Итак, открывая книгу, я проверяю, что NativeSheet.UsedRange пусто или возвращаю ноль (Ничего в Visual Basic), в противном случае я уже получил использованные «собственные ячейки» в памяти, так что остается только добавить их во внутреннюю CellCollection словарь при индексации их по соответствующему адресу.

Наконец, Ленивая инициализация Шаблон проектирования на помощь!=)

public class Sheet : ISheet {
    public Worksheet(Microsoft.Office.Interop.Excel.Worksheet nativeSheet) {
        NativeSheet = nativeSheet;
        Cells = new CellCollection(this);
    }

    public Microsoft.Office.Interop.Excel.Worksheet NativeSheet { get; private set; }

    public CellCollection Cells { get; private set; }
}

public sealed class CellCollection {
    private IDictionary<string, ICell> _cells;
    private ReadOnlyDictionary<string, ICell> _readonlyCells;

    public CellCollection(ISheet sheet) {
        _cells = new Dictionary<string, ICell>();
        _readonlyCells = new ReadonlyDictionary<string, ICell>(_cells);
        Sheet = sheet;
    }

    public readonly ReadOnlyDictionary<string, ICell> Cells(string addresses) {
        get {
            if (string.IsNullOrEmpty(addresses) || 0 = address.Trim().Length)
                throw new ArgumentNullException("addresses");

            if (!Regex.IsMatch(addresses, "(([A-Za-z]{1,2,3}[0-9]*)[:,]*)"))
                throw new FormatException("addresses");

            foreach(string address in addresses.Split(",") {
                Microsoft.Office.Interop.Excel.Range range = Sheet.NativeSheet.Range(address)

                foreach(Microsoft.Office.Interop.Excel.Range cell in range) {
                    ICell c = null;
                    if (!_cells.TryGetValue(cell.Address(false, false), c)) { 
                        c = new Cell(cell);
                        _cells.Add(c.Name, c);
                    }
                }
            }

            return _readonlyCells;
        }
    }

    public readonly ISheet Sheet { get; private set; }
}

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

После написания этой коллекции я смог прийти к ожидаемому поведению,Теперь я попытаюсь реализовать некоторые из интерфейсов .NET, чтобы сделать его пригодным для использования с некоторыми IEnumerable, IEnumerable<T>, ICollection, ICollection<T> и т. Д., Чтобы его можно было соответственно рассматривать как истинную коллекцию .NET.

Не стесняйтесь комментировать и вносить конструктивные альтернативы и / или изменения в этот код, чтобы он мог стать даже больше, чем он есть в настоящее время.

Я действительно надеюсь, что когда-нибудь это послужит цели.

Спасибо за чтение!=) * +1058 *

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...