最快的 excel 复制大范围的互操作方式,对其进行排序并删除不必要的列

Quickest excel interop way to copy a big range, sort it and remove unnecessay columns

我有一个 c# 代码可以生成一个非常大的数组,我将其写入 excel 工作表。然后我在同一个工作簿中使用相同的大数组创建另一个工作表,并对后者进行排序:

using Microsoft.Office.Interop.Excel;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Excel = Microsoft.Office.Interop.Excel;

namespace ConsoleApp1
{
    class Program
    {
        static object[,] returnDummyVariant()
        {
            object[,] res = new object[4,3];

            res[0, 0] = "Surname";
            res[0, 1] = "Height";
            res[0, 2] = "Age";

            res[1, 0] = "Julian";
            res[1, 1] = "185";
            res[1, 2] = "39";

            res[2, 0] = "Mark";
            res[2, 1] = "173";
            res[2, 2] = "63";

            res[3, 0] = "Patrick";
            res[3, 1] = "193";
            res[3, 2] = "23";

            return res;
        }

        private static Excel.Range WriteTopLeft(object[,] variant, Excel.Range topLeft)
        {
            if (variant == null) return null;

            int nL = variant.GetLength(0);
            int nC = variant.GetLength(1);

            if (nL * nC == 0) return null;

            var worksheet = topLeft.Worksheet;
            var writeRange = worksheet.Range[topLeft, topLeft.Offset[nL - 1, nC - 1]];

            writeRange.Value2 = variant;

            return writeRange;
        }

        static void Main(string[] args)
        {
            Application excel = new Application();

            string path = @"paht\to\file\Book1.xlsx";

            Workbook wb = excel.Workbooks.Open(path);

            var wsData = (Excel.Worksheet)wb.Worksheets.Item[1];
            wsData.Name = "Data";

            var variant = returnDummyVariant();
            var rData = WriteTopLeft(variant, wsData.Cells[1, 1]);

            int rows = rData.Rows.Count;
            int columns = rData.Columns.Count;

            var ws = (Excel.Worksheet)wb.Worksheets.Add();
            ws.Name = "Sorted Data";
            rData = WriteTopLeft(variant, ws.Cells[1, 1]);

            rData.Sort(rData.Columns[2, Type.Missing], Excel.XlSortOrder.xlDescending,
                Type.Missing, Excel.XlSortOrder.xlAscending, Excel.XlSortOrder.xlAscending,
                Type.Missing, Excel.XlSortOrder.xlAscending,
                Excel.XlYesNoGuess.xlGuess, Type.Missing, Type.Missing,
                Excel.XlSortOrientation.xlSortColumns, Excel.XlSortMethod.xlPinYin,
                Excel.XlSortDataOption.xlSortNormal,
                Excel.XlSortDataOption.xlSortNormal,
                Excel.XlSortDataOption.xlSortNormal);

            wb.Save();
            wb.Close();
        }
    }
}

.Sort 位触发异常:System.Runtime.InteropServices.COMException: 'Reference isn't valid.',我找不到原因。

我在这里和那里尝试了几次更正,但都没有成功。

首先,作为一种好的做法,您应该捕获抛出的异常并查找任何其他信息 - 这将是此异常的 HRESULT。

虽然在这种情况下,获得的信息并不是特别有用 - 错误代码 0x800a03ec,用于多个故障。

查看 Sort 方法的文档,我发现您的第 4 个参数不正确 - 它应该只在排序数据透视表时使用,否则应该作为 Type.Missing.[=15 传递=]

rData.Sort(rData.Columns[2, Type.Missing], Excel.XlSortOrder.xlDescending,
            Type.Missing, Type.Missing,  Excel.XlSortOrder.xlAscending,
            Type.Missing, Excel.XlSortOrder.xlAscending,
            Excel.XlYesNoGuess.xlGuess, Type.Missing, Type.Missing,
            Excel.XlSortOrientation.xlSortColumns, Excel.XlSortMethod.xlPinYin,
            Excel.XlSortDataOption.xlSortNormal,
            Excel.XlSortDataOption.xlSortNormal,
            Excel.XlSortDataOption.xlSortNormal);

您还可以通过使用命名参数来简化方法调用,然后它将为您省略的参数使用适当的默认值:

        rData.Sort(rData.Columns[2, Type.Missing],
             XlSortOrder.xlDescending, 
             Header: XlYesNoGuess.xlGuess, 
             Orientation: XlSortOrientation.xlSortColumns);

另见 https://docs.microsoft.com/en-us/visualstudio/vsto/how-to-programmatically-sort-data-in-worksheets?view=vs-2019

此外,您应该退出应用程序 - excel.Quit();在你的申请结束时。默认情况下,Excel 的实例会打开但不会显示 - 如果您不退出该应用程序,它将保持打开状态,直到您关闭您的 PC - 多次尝试 运行 您的应用程序然后打开启动 TaskManager,您将看到相同数量的 Excel 个进程。

如果您的应用程序像上面的代码一样相对较小,那么在整理 Excel COM 对象方面应该没有任何问题,如果您的代码更复杂并且您使用了很多对象(范围、工作表)等都是 COM 对象)那么你应该确保它们通过使用 Marshal.ReleaseComObject 并将引用设置为 null 从内存中释放 - 有些人也建议调用 GC.Collect 两次 - 这里有很多讨论:How do I properly clean up Excel interop objects?