您好, 欢迎来到 !    登录 | 注册 | | 设为首页 | 收藏本站

在实时(未保存)Excel数据和C#对象之间进行交互的最快方法

在实时(未保存)Excel数据和C#对象之间进行交互的最快方法

如果C#应用程序是独立的应用程序,那么您将始终涉及跨进程封送处理,这将使您无法将语言从C#切换为C ++,从而无法进行任何优化。在这种情况下,请坚持使用您最喜欢的语言,这听起来像是C#。

但是,如果您愿意制作一个 Excel 中运行的加载项,则您的操作将避免跨进程调用,并且运行速度快约50倍。

如果您在Excel中作为加载项运行,则VBA是最快的选项之一,但是它仍然涉及COM,因此使用XLL加载项进行C ++调用将是最快的。但是就调用Excel对象模型而言,VBA仍然相当快。但是,对于实际的计算速度,VBA作为pcode运行,而不是作为完全编译的代码运行,因此执行速度比本地代码慢2-3倍。这听起来很糟糕,但这不是因为典型的Excel加载项或应用程序花费的绝大部分执行时间都涉及对Excel对象模型的调用,因此VBA与完全编译的COM加载项(例如使用本机编译的VB 6.0,速度只会慢5-15%,这并不引人注意。

VB 6.0是一种经过编译的COM方法,与非Excel相关的调用相比,运行速度比VBA快2-3倍,但是此时VB 6.0已使用了12年左右,并且无法在64位模式下运行,例如,如果安装Office 2010,可以安装运行32位或64位。目前64位Excel的使用量很小,但使用量会增加,因此出于这个原因,我将避免使用VB 6.0。

C#如果以Excel加载项的形式在进程内运行,则对Excel对象模型的调用将以VBA一样快的速度执行,并且非Excel调用的执行速度比VBA快2-3倍-如果运行不带垫片。但是,Microsoft建议的方法是完全匀场运行,例如,通过使用COM Shim向导。通过填充,可以保护Excel免受代码(如果有错误)的侵害,并且可以完全保护您的代码不受其他第三方加载项的影响,否则可能会导致问题。但是,不利的一面是,匀场解决方案在单独的AppDomain中运行,这需要跨AppDomain封送处理,这会导致执行速度损失约40倍-在许多情况下这非常明显。

使用Visual Studio Tools for Office(VSTO)的加载项将自动加载到填充程序中,并在单独的AppDomain中执行。如果使用VSTO,这是不可避免的。因此,对Excel对象模型的调用也将导致执行速度下降大约40倍。VSTO是用于生成非常丰富的Excel加载项的出色系统,但是执行速度是它对诸如您的应用程序的缺点。

ExcelDna是一个免费的开源项目,允许您使用C#代码,然后将其转换为使用C 代码的XLL加载项。也就是说,ExcelDna解析您的C# 代码并为您创建所需的C 代码。我自己没有使用过它,但是我熟悉该过程,并且给人留下了深刻的印象。ExcelDna受到使用它的人的好评。[编辑:请按照以下Govert的注释进行以下更正:“嗨,迈克-我想添加一个小更正以阐明Excel- Dna的实现:所有托管到Excel胶水都可以在运行时使用反射在托管程序集中运行-不需要额外的预编译步骤或C ++代码生成;而且,即使Excel- Dna使用.NET,在与Excel通话时也无需涉及任何COM互操作- 作为.xll,可以直接从.NET使用本机接口(尽管您也可以根据需要使用COM。)这使高性能的UDF和宏成为可能。” – Govert]

您可能还需要查看Add-in Express。它不是免费的,但是它允许您使用C#进行编码,尽管它可以将解决方案填充到单独的AppDomain中,但我认为它的执行速度非常出色。如果我正确地理解了它的执行速度,那么我不确定Add- in Express是如何做到这一点的,但是它可能正在利用称为FastPath AppDomain封送处理的优势。但是,由于我对Add-in Express不太熟悉,所以请不要对我进行任何引用。不过,您应该检查一下并进行自己的研究。 [编辑:阅读Charles Williams的答案,看来Add-in Express启用了COM和C API访问。 Govert指出,Excel DNA还支持COM和更快的C API访问。因此,您可能希望同时检查两者并将它们与ExcelDna进行比较。]

我的建议是研究Add-in 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访问器方法一步来读取范围中的值数组。这样做然后循环遍历数组中的值比循环遍历范围内单元格中的值要快得多。

c# 2022/1/1 18:19:08 有481人围观

撰写回答


你尚未登录,登录后可以

和开发者交流问题的细节

关注并接收问题和回答的更新提醒

参与内容的编辑和改进,让解决方法与时俱进

请先登录

推荐问题


联系我
置顶