Самый быстрый способ взаимодействия между живыми (несохраненными) данными Excel и объектами C # - PullRequest
29 голосов
/ 01 октября 2010

Я хочу знать, какой самый быстрый способ чтения и записи данных в открытую книгу Excel и из нее в объекты c #. Исходя из этого, я хочу разработать приложение на c #, которое используется из Excel и использует данные, хранящиеся в Excel.

Бизнес-логика будет находиться в приложении c #, но данные будут храниться в книге Excel. Пользователь будет использовать Excel и щелкнет кнопку (или сделает что-то подобное) в книге Excel, чтобы запустить приложение c #. Затем приложение c # будет считывать данные из рабочей книги Excel, обрабатывать их, а затем записывать данные обратно в рабочую книгу Excel.
Может быть множество блоков данных, которые необходимо прочитать и записать обратно в книгу Excel, но обычно они имеют относительно небольшой размер, например, 10 строк и 20 столбцов. Иногда может потребоваться обработка большого списка данных, порядка 50 000 строк и 40 столбцов.

Я знаю, что это относительно легко сделать, используя VSTO, но я хочу знать, какое решение является самым быстрым (но все же надежным и элегантным), и получить представление о скорости. Я не против, если решение рекомендует использовать сторонние продукты или использовать C ++.

Очевидным решением является использование VSTO или взаимодействия, но я не знаю, какова производительность по сравнению с VBA, которую я сейчас использую для чтения данных, или есть ли другие решения.

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

http://www.experts -exchange.com / Microsoft / Разработка / VSTO / Q_23635459.html

Спасибо.

Ответы [ 6 ]

40 голосов
/ 06 октября 2010

Я приму это как вызов и сделаю ставку, что самый быстрый способ переместить ваши данные между Excel и C # - это использовать Excel-ДНК - http://exceldna.codeplex.com. (Отказ от ответственности: я разрабатываю Excel-ДНК. Но этовсе еще верно ...)

Поскольку он использует собственный интерфейс .xll, он пропускает все издержки интеграции COM, которые вы имели бы с VSTO или другим подходом на основе COM-надстроек.С Excel-Dna вы можете создать макрос, который подключается к кнопке меню или ленты, которая считывает диапазон, обрабатывает его и записывает обратно в диапазон в Excel.Все они используют собственный интерфейс Excel из C #, а не объект COM в поле зрения.

Я сделал небольшую тестовую функцию, которая переносит текущий выбор в массив, возводит в квадрат каждое число в массиве и записывает результатв лист 2, начиная с ячейки A1.Вам просто нужно добавить (бесплатную) среду выполнения Excel-ДНК, которую вы можете загрузить из http://exceldna.codeplex.com.

Я читаю в C #, обрабатываю и записываю обратно в Excel диапазон в миллион ячеек менее чем за секунду.Это достаточно быстро для вас?

Моя функция выглядит следующим образом:

using ExcelDna.Integration;
public static class RangeTools {

[ExcelCommand(MenuName="Range Tools", MenuText="Square Selection")]
public static void SquareRange()
{
    object[,] result;

    // Get a reference to the current selection
    ExcelReference selection = (ExcelReference)XlCall.Excel(XlCall.xlfSelection);
    // Get the value of the selection
    object selectionContent = selection.GetValue();
    if (selectionContent is object[,])
    {
        object[,] values = (object[,])selectionContent;
        int rows = values.GetLength(0);
        int cols = values.GetLength(1);
        result = new object[rows,cols];

        // Process the values
        for (int i = 0; i < rows; i++)
        {
            for (int j = 0; j < cols; j++)
            {
                if (values[i,j] is double)
                {
                    double val = (double)values[i,j];
                    result[i,j] = val * val;
                }
                else
                {
                    result[i,j] = values[i,j];
                }
            }
        }
    }
    else if (selectionContent is double)
    {
        double value = (double)selectionContent;
        result = new object[,] {{value * value}}; 
    }
    else
    {
        result = new object[,] {{"Selection was not a range or a number, but " + selectionContent.ToString()}};
    }

    // Now create the target reference that will refer to Sheet 2, getting a reference that contains the SheetId first
    ExcelReference sheet2 = (ExcelReference)XlCall.Excel(XlCall.xlSheetId, "Sheet2"); // Throws exception if no Sheet2 exists
    // ... then creating the reference with the right size as new ExcelReference(RowFirst, RowLast, ColFirst, ColLast, SheetId)
    int resultRows = result.GetLength(0);
    int resultCols = result.GetLength(1);
    ExcelReference target = new ExcelReference(0, resultRows-1, 0, resultCols-1, sheet2.SheetId);
    // Finally setting the result into the target range.
    target.SetValue(result);
}
}
38 голосов
/ 02 октября 2010

Если приложение C # является автономным приложением, то вы всегда будете задействованы в межпроцессном маршалинге, который сократит любые оптимизации, которые вы можете выполнить, переключая языки, скажем, с C # на C ++. В этой ситуации придерживайтесь своего наиболее предпочтительного языка, который звучит как C #.

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

Если вы запускаете в Excel как надстройку, то VBA является одним из самых быстрых вариантов, но он все еще включает COM, и поэтому вызовы C ++ с использованием надстройки XLL будут самыми быстрыми. Но VBA все еще довольно быстр с точки зрения обращений к объектной модели Excel. Что касается фактической скорости вычислений, однако, VBA работает как pcode, а не как полностью скомпилированный код, и поэтому выполняется примерно в 2-3 раза медленнее, чем собственный код. Это звучит очень плохо, но это не потому, что подавляющее большинство времени выполнения, затрачиваемого на обычную надстройку или приложение Excel, связано с вызовами объектной модели Excel, поэтому VBA против полностью скомпилированной надстройки COM, скажем, с использованием изначально скомпилированный VB 6.0 будет работать только на 5-15% медленнее, что не заметно.

VB 6.0 - это скомпилированный подход COM, который работает в 2-3 раза быстрее, чем VBA для вызовов, не связанных с Excel, но на данный момент VB 6.0 уже около 12 лет, и он не будет работать в 64-битном режиме, например, при установке Office 2010, который можно установить для запуска 32-разрядной или 64-разрядной версии. Использование 64-битного Excel на данный момент незначительно, но будет расти, поэтому по этой причине я бы отказался от VB 6.0.

C #, если при работе в качестве надстройки Excel выполняются вызовы объектной модели Excel так же быстро, как VBA, и выполняются вызовы, не относящиеся к Excel, в 2-3 раза быстрее, чем VBA - если выполняется без помех. Тем не менее, рекомендуемый Microsoft подход заключается в том, чтобы работать в полном режиме, например, с помощью COM Shim Wizard . Благодаря использованию Shimmed Excel защищен от вашего кода (если он неисправен), а ваш код полностью защищен от других сторонних надстроек, которые в противном случае могли бы вызвать проблемы. Недостатком этого является, однако, то, что решение с ограниченным доступом работает в отдельном домене приложений, что требует маршалинга между доменами приложений, что влечет за собой снижение скорости выполнения примерно в 40 раз, что очень заметно во многих контекстах.

Надстройки с использованием Visual Studio Tools for Office (VSTO) автоматически загружаются в оболочку и выполняются в отдельном домене приложения. Этого нельзя избежать при использовании VSTO. Следовательно, вызовы объектной модели Excel также могут привести к снижению скорости выполнения примерно в 40 раз. VSTO - великолепная система для создания очень богатых надстроек Excel, но скорость выполнения - ее слабость для таких приложений, как ваше.

ExcelDna - это бесплатный проект с открытым исходным кодом, который позволяет использовать код C #, который затем преобразуется для вас в надстройку XLL, использующую код C ++. То есть ExcelDna анализирует ваш код C # и создает для вас необходимый код C ++. Я не использовал это сам, но я знаком с процессом, и это очень впечатляет. ExcelDna получает очень хорошие отзывы от тех, кто его использует. [Редактировать: обратите внимание на следующее исправление в соответствии с комментариями Говерта ниже: «Привет, Майк. Я хочу добавить небольшое исправление, чтобы прояснить реализацию Excel-ДНК: все склейки от управляемого к Excel работают во время выполнения из управляемой сборки с использованием отражение - нет дополнительного этапа предварительной компиляции или генерации кода на C ++. Кроме того, хотя Excel-Dna использует .NET, при общении с Excel не требуется никакого COM-взаимодействия - в качестве .xll можно напрямую использовать собственный интерфейс из .NET (хотя вы также можете использовать COM, если хотите). Это делает возможными высокопроизводительные пользовательские функции и макросы ». - Говерт]

Вы также можете посмотреть на надстройку Express.Это не бесплатно, но это позволит вам кодировать на C #, и, хотя оно объединяет ваше решение в отдельный домен приложений, я считаю, что скорость его выполнения является выдающейся.Если я правильно понимаю скорость его выполнения, то я не уверен, как это делает Add-in Express, но он может воспользоваться преимуществами маршалинга FastPath AppDomain.Однако не цитируйте меня по этому поводу, поскольку я не очень знаком с надстройкой Express.Вы должны проверить это и сделать свое собственное исследование. [Редактировать: Читая ответ Чарльза Уильямса, похоже, что Add-in Express обеспечивает доступ к COM и C API.А Говерт заявляет, что Excel DNA также обеспечивает как COM, так и более быстрый доступ к C API.Так что вы, вероятно, захотите проверить оба и сравнить их с ExcelDna.]

Мой совет - изучить надстройку Express и ExcelDna.Оба подхода позволили бы вам кодировать с использованием C #, с которым вы, кажется, наиболее знакомы.

Другой основной вопрос - как вы делаете свои звонки.Например, Excel работает очень быстро, обрабатывая весь диапазон данных, передаваемых в виде массива.Это гораздо эффективнее, чем проходить по клеткам индивидуально.Например, в следующем коде используется метод доступа Excel.Range.set_Value для назначения массива значений 10 x 10 диапазону ячеек 10 x 10 в одном кадре:

void AssignArrayToRange()
{
    // Create the array.
    object[,] myArray = new object[10, 10];

    // Initialize the array.
    for (int i = 0; i < myArray.GetLength(0); i++)
    {
        for (int j = 0; j < myArray.GetLength(1); j++)
        {
            myArray[i, j] = i + j;
        }
    }

    // Create a Range of the correct size:
    int rows = myArray.GetLength(0);
    int columns = myArray.GetLength(1);
    Excel.Range range = myWorksheet.get_Range("A1", Type.Missing);
    range = range.get_Resize(rows, columns);

    // Assign the Array to the Range in one shot:
    range.set_Value(Type.Missing, myArray);
}

Можно аналогичным образомиспользуйте метод доступа Excel.Range.get_Value для чтения массива значений из диапазона за один шаг.Выполнение этого и последующее циклическое переключение значений в массиве происходит намного быстрее, чем циклическое повторение значений в ячейках диапазона по отдельности.

4 голосов
/ 04 октября 2010

В дополнение к комментариям Майка Розенблюма об использовании массивов, я хотел бы добавить, что я использовал тот же подход (массивы VSTO +), и когда я измерял его, фактическая скорость чтения была в пределах миллисекунд. Просто не забудьте отключить обработку событий и обновление экрана перед чтением / записью, и не забудьте снова включить ее после завершения операции.

Используя C #, вы можете создавать массивы на основе 1 точно так же, как это делает сам Excel VBA. Это очень полезно, особенно потому, что даже в VSTO, когда вы извлекаете массив из объекта Excel.Range, массив основывается на 1, поэтому сохранение ориентированных на Excel массивов на основе 1 помогает избежать необходимости всегда проверять, является ли массив является единичным или основанным на нуле. (Если позиция столбца в массиве имеет для вас значение, то иметь дело с массивами на основе 0 и 1 может быть настоящей болью).

Вообще чтение Excel.Range в массив будет выглядеть примерно так:

var myArray = (object[,])range.Value2;


Мой вариант записи массива Майка Розенблюма использует массив на основе 1, подобный следующему:

int[] lowerBounds = new int[]{ 1, 1 };
int[] lengths = new int[] { rowCount, columnCount };  
var myArray = 
    (object[,])Array.CreateInstance(typeof(object), lengths, lowerBounds);

var dataRange = GetRangeFromMySources();

// this example is a bit too atomic; you probably want to disable 
// screen updates and events a bit higher up in the call stack...
dataRange.Application.ScreenUpdating = false;
dataRange.Application.EnableEvents = false;

dataRange = dataRange.get_Resize(rowCount, columnCount);
dataRange.set_Value(Excel.XlRangeValueDataType.xlRangeValueDefault, myArray);

dataRange.Application.ScreenUpdating = true;
dataRange.Application.EnableEvents = true;
3 голосов
/ 05 октября 2010

Во-первых, ваше решение не может быть Excel UDF (пользовательская функция).В наших руководствах мы даем следующее определение: «UDF Excel используются для создания пользовательских функций в Excel, чтобы конечный пользователь мог использовать их в формулах».Я не возражаю, если вы предложите более точное определение :)

Это определение показывает, что UDF не может добавить кнопку в пользовательский интерфейс (я знаю, что XLL могут изменять пользовательский интерфейс CommandBar) или перехватывать сочетания клавиш, а такжеСобытия Excel.

То есть ExcelDNA выходит за рамки, поскольку он предназначен для разработки надстроек XLL.То же самое относится к функциональности надстройки Express, нацеленной на Excel, поскольку она позволяет разрабатывать надстройки XLL и надстройки Excel Automation.

Поскольку вам нужно обрабатывать события Excel, ваше решение может быть автономным приложением, но существуют очевидные ограничения такого подхода.Единственный реальный способ - создать надстройку COM;это позволяет обрабатывать события Excel и добавлять пользовательские элементы в пользовательский интерфейс Excel.У вас есть три возможности:

  • VSTO
  • Надстройка Express (функция надстройки COM)
  • Совместно используемая надстройка (см. Соответствующий пункт в разделе «Новые»).Диалоговое окно проекта в VS)

Если говорить о разработке надстройки Excel COM, вышеупомянутые 3 инструмента предоставляют различные функции: визуальные дизайнеры, шимминг и т. Д. Но я не думаю, что они отличаются вскорость доступа к объектной модели Excel.Скажем, я не знаю (и не могу себе представить), почему получение COM-объекта из AppDomain по умолчанию должно отличаться от получения того же COM-объекта из другого AppDomain.Кстати, вы можете проверить, влияет ли шимминг на скорость работы, создав совместно используемую надстройку, а затем используя COM Shim Wizard для ее шиммирования.

Скорость II.Как я писал вам вчера: «Лучший способ ускорить чтение и запись в диапазон ячеек - это создать переменную типа Excel.Range, ссылающуюся на этот диапазон, а затем прочитать / записать массив из / в свойство Valueпеременной. "Но вопреки тому, что говорит Франческо, я не приписываю это ВСТО;это особенность объектной модели Excel.

Скорость III.Самые быстрые пользовательские функции Excel написаны на родном C ++, а не на любом языке .NET.Я не сравнивал скорость надстройки XLL, созданной ExcelDNA и Add-in Express;Я не думаю, что вы найдете здесь существенную разницу.

Подводя итог.Я убежден, что вы ошибаетесь: надстройки COM, основанные на надстройках Express, VSTO или Shared Add-in, должны читать и записывать ячейки Excel с одинаковой скоростью.Я буду рад (искренне), если кто-то опровергнет это утверждение.

Теперь по другим вашим вопросам.VSTO не позволяет разрабатывать надстройку COM, поддерживающую Office 2000-2010.Требуется три разных кодовых базы и как минимум две версии Visual Studio для полной поддержки Office 2003-2010;вам нужно иметь крепкие нервы и часть удачи для развертывания надстройки на основе VSTO для Excel 2003. С помощью надстройки Express вы создаете надстройку COM для всех версий Office с единой кодовой базой;Надстройка Express предоставляет вам проект установки, который готов установить вашу надстройку в Excel 2000-2010 (32-разрядная и 64-разрядная версия);Развертывание ClickOnce также на борту.

VSTO превосходит Add-in Express в одной области: он позволяет создавать так называемые надстройки уровня документа.Представьте себе рабочую книгу или шаблон с некоторым кодом .NET позади него;Однако я не удивлюсь, если развертывание таких вещей станет кошмаром.

О событиях Excel.Все события Excel перечислены в MSDN, например, см. События Excel 2007

С уважением, из Беларуси (GMT + 2),

Андрей Смолин Руководитель надстройки Express Team

3 голосов
/ 02 октября 2010

Самый быстрый интерфейс для данных Excel - это API C.Есть ряд продуктов, которые связывают .NET с Excel с помощью этого интерфейса.

2 продукта, которые мне нравятся, это Excel DNA (бесплатный и открытый) и Addin Express (коммерческийпродукт и имеет как интерфейс API C, так и интерфейс COM).

0 голосов
/ 01 октября 2010

Я использовал VBA-код (макрос) для сбора и сжатия данных и получения этих данных за один вызов C # и наоборот. Вероятно, это будет наиболее эффективный подход.

Используя C #, вам всегда нужно использовать маршаллинг. При использовании VSTO или COM Interop нижележащий коммуникационный уровень (распределение служебных данных) одинаков.

В VBA (Visual Basic для приложений) вы работаете непосредственно с объектами в Excel. Так что доступ к этим данным всегда будет быстрее.

Но .... Если у вас есть данные в C #, манипулирование этими данными может быть намного быстрее.

Если вы используете VB6 или C ++, вы также проходите через интерфейс COM и столкнетесь с межпроцессным маршалингом.

Итак, вы ищете метод для минимизации межпроцессных вызовов и маршалинга.

...