Самый быстрый способ взаимодействия между живыми (несохраненными) данными Excel и объектами C#

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

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

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

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

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

http://www.experts-exchange.com/Microsoft/Development/VSTO/Q_23635459.html

спасибо.

6 ответов


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

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

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

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

C#, если выполняется в процессе как надстройка Excel, будет выполнять вызовы объектной модели Excel так же быстро, как VBA, и выполнять вызовы, отличные от Excel, на 2-3x быстрее, чем VBA-если выполняется unshimmed. Однако подход, рекомендуемый Microsoft, заключается в том, чтобы запустить полностью shimmed, например, используя com Shim Wizard. Будучи shimmed, Excel защищен от вашего кода (если это и ваш код полностью защищен от других сторонних надстроек, которые в противном случае могли бы потенциально вызвать проблемы. Недостатком этого, однако, является то, что решение shimmed работает в отдельном AppDomain, который требует маршалинга cross-AppDomain, который несет штраф скорости выполнения около 40x-что очень заметно во многих контекстах.

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

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

вы также можете посмотреть надстройку Express. Это не бесплатно, но это позволит вам кодировать на C# , и хотя он помещает ваше решение в отдельный AppDomain, я считаю, что это выполнение скорость выдающаяся. Если я правильно понимаю скорость его выполнения, то я не уверен, как Add-In Express делает это, но он может использовать что-то под названием fastpath AppDomain marshaling. Однако не цитируйте меня ни по одному из этих вопросов, поскольку я не очень хорошо знаком с Add-In Express. Вы должны проверить его и сделать свое собственное исследование. [Edit: чтение ответа Чарльза Уильямса, похоже, надстройка Express включает доступ к COM и C API. И Govert утверждает, что Excel ДНК также включает доступ к API COM и fastrer C. Поэтому вы, вероятно, захотите проверить оба и сравнить их с ExcelDna.]

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

другая основная проблема заключается в том, как вы делаете свои звонки. Например, Excel очень быстр при обработке всего диапазона данных, передаваемых взад и вперед в виде массива. Это значительно эффективнее чем петля через клетки индивидуально. Например, следующий код использует Excel.Диапазон.метод доступа 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.Диапазон.метод доступа get_Value для чтения массива значений из диапазона за один шаг. Выполнение этого, а затем цикл через значения в массиве значительно быстрее, чем цикл через значения в ячейках диапазон индивидуально.


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

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

Я сделал небольшую тестовую функцию, которая принимает текущий выбор в массив, квадраты каждого числа в массиве и записывает результат в лист 2, начиная с ячейки A1. Вам просто нужно добавить (бесплатно) Excel-Dna runtime, который вы можете скачать с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);
}
}

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

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

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

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;

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

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


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

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

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

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

  • настройка
  • надстройка Express (функциональность надстройки COM)
  • общая надстройка (см. соответствующий пункт в диалоговом окне новый проект в VS)

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

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

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

подводя итог. Я убежден, что вы находитесь на неправильном пути: надстройки COM на основе надстройки Express, VSTO или Shared Надстройка должна читать и писать ячейки 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 бьет надстройку Express в одной области: она позволяет создавать так называемые надстройки уровня документа. Представьте себе книгу или шаблон с некоторым .NET-кодом за ним; я не удивлюсь, если развертывание таких вещей является кошмар.

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

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

Андрей Смолин Надстройка Express Team Leader


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

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

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

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

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

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