跳至页脚内容
使用 IRONXL

如何在 C# 中创建 Excel 数据透视表?

在 C# 中创建 Excel 数据透视表既可以使用 Office Interop(需要安装 Microsoft Office),也可以使用 IronXL 等独立运行的现代库,其中 IronXL 为DevOps环境提供了更优越的部署灵活性和跨平台支持。

以编程方式生成数据透视表需要使用 C# 互操作 及其 Office 依赖项,或者使用像IronXL这样可以独立工作的现代库。 本教程演示了这两种方法,重点介绍了为什么开发人员越来越多地选择 IronXL 而不是传统的互操作方法,尤其是在部署到Docker 容器AzureAWS等云环境时。

在本文中,我们将学习如何编辑、创建、设计和计算透视表和分组,并进行自动分析和错误处理——所有这些都将保持DevOps工程师所需的部署简易性。

什么是Excel数据透视表?

数据透视表是Excel最强大的工具之一。 它是一种汇总大型数据集的简便方法,使其在 .NET 应用程序的数据分析中具有不可估量的价值。 数据透视表可以帮助您轻松显示、理解和分析数值数据。 它们不仅在 Excel 中可用,而且在其他程序中也可用,例如 Google Sheets、Apple Numbers 和CSV Exports 。 它们提供了一种查看数据概览的解决方案——充当数据控制台,让人们能够以有意义的方式查看自己的信息。

对于容器化应用程序,以编程方式创建数据透视表可以消除在 Docker 镜像中安装 Excel 的需要,从而显著减小容器大小和部署复杂性。 这种方法与现代 CI/CD 流水线和容器部署策略完美契合。

让我们先来探讨一下创建数据透视表的错误方法,然后再学习 C# 中的正确方法:

如何使用 C# 互操作 在 Excel 表格中创建数据透视表?

C# Excel Interop 通过 COM 自动化提供对 Excel 数据透视表功能的直接访问。 以下是许多开发人员在寻找用于在 C# 中生成数据透视表的工具时遇到的传统方法:

为什么这种方法在 .NET 中被认为已经过时?

using Excel = Microsoft.Office.Interop.Excel;
using System.Runtime.InteropServices;
// Create Excel application instance
Excel.Application xlApp = new Excel.Application();
Excel.Workbook xlWorkbook = xlApp.Workbooks.Open(@"C:\Data\SalesData.xlsx");
Excel.Worksheet xlSheet = (Excel.Worksheet)xlWorkbook.Sheets[1];
Excel.Worksheet xlPivotSheet = (Excel.Worksheet)xlWorkbook.Sheets.Add();
// Define data range for pivot table
Excel.Range dataRange = xlSheet.UsedRange;
// Row area and column area 
// Create pivot cache and pivot table
Excel.PivotCache pivotCache = xlWorkbook.PivotCaches().Create(
    Excel.XlPivotTableSourceType.xlDatabase,
    dataRange,
    Type.Missing);
Excel.PivotTable pivotTable = pivotCache.CreatePivotTable(
    xlPivotSheet.Cells[3, 1],
    "SalesPivot",
    Type.Missing,
    Type.Missing); // fields by field
// Configure pivot table fields
Excel.PivotField productField = (Excel.PivotField)pivotTable.PivotFields("Product");
productField.Orientation = Excel.XlPivotFieldOrientation.xlRowField;
productField.Position = 1;
Excel.PivotField regionField = (Excel.PivotField)pivotTable.PivotFields("Region");
regionField.Orientation = Excel.XlPivotFieldOrientation.xlColumnField;
regionField.Position = 1;
Excel.PivotField salesField = (Excel.PivotField)pivotTable.PivotFields("Sales");
pivotTable.AddDataField(salesField, "Sum of Sales", Excel.XlConsolidationFunction.xlSum);
// Save and cleanup
xlWorkbook.SaveAs(@"C:\Data\PivotReport.xlsx");
xlWorkbook.Close();
xlApp.Quit();
// Release COM objects to prevent memory leaks
Marshal.ReleaseComObject(pivotTable);
Marshal.ReleaseComObject(pivotCache);
Marshal.ReleaseComObject(xlPivotSheet);
Marshal.ReleaseComObject(xlSheet);
Marshal.ReleaseComObject(xlWorkbook);
Marshal.ReleaseComObject(xlApp);
using Excel = Microsoft.Office.Interop.Excel;
using System.Runtime.InteropServices;
// Create Excel application instance
Excel.Application xlApp = new Excel.Application();
Excel.Workbook xlWorkbook = xlApp.Workbooks.Open(@"C:\Data\SalesData.xlsx");
Excel.Worksheet xlSheet = (Excel.Worksheet)xlWorkbook.Sheets[1];
Excel.Worksheet xlPivotSheet = (Excel.Worksheet)xlWorkbook.Sheets.Add();
// Define data range for pivot table
Excel.Range dataRange = xlSheet.UsedRange;
// Row area and column area 
// Create pivot cache and pivot table
Excel.PivotCache pivotCache = xlWorkbook.PivotCaches().Create(
    Excel.XlPivotTableSourceType.xlDatabase,
    dataRange,
    Type.Missing);
Excel.PivotTable pivotTable = pivotCache.CreatePivotTable(
    xlPivotSheet.Cells[3, 1],
    "SalesPivot",
    Type.Missing,
    Type.Missing); // fields by field
// Configure pivot table fields
Excel.PivotField productField = (Excel.PivotField)pivotTable.PivotFields("Product");
productField.Orientation = Excel.XlPivotFieldOrientation.xlRowField;
productField.Position = 1;
Excel.PivotField regionField = (Excel.PivotField)pivotTable.PivotFields("Region");
regionField.Orientation = Excel.XlPivotFieldOrientation.xlColumnField;
regionField.Position = 1;
Excel.PivotField salesField = (Excel.PivotField)pivotTable.PivotFields("Sales");
pivotTable.AddDataField(salesField, "Sum of Sales", Excel.XlConsolidationFunction.xlSum);
// Save and cleanup
xlWorkbook.SaveAs(@"C:\Data\PivotReport.xlsx");
xlWorkbook.Close();
xlApp.Quit();
// Release COM objects to prevent memory leaks
Marshal.ReleaseComObject(pivotTable);
Marshal.ReleaseComObject(pivotCache);
Marshal.ReleaseComObject(xlPivotSheet);
Marshal.ReleaseComObject(xlSheet);
Marshal.ReleaseComObject(xlWorkbook);
Marshal.ReleaseComObject(xlApp);
$vbLabelText   $csharpLabel

此 Interop 示例创建了一个原生 Excel 数据透视表,其中产品为行,地区为列,销售额汇总在数据区域中。 虽然功能正常,但这种方法需要安装Microsoft Office并仔细管理COM对象。 微软的文档解释了为什么这种方法已经过时。 从DevOps角度来看,这种方法尤其成问题,因为它无法有效地容器化——你无法在Linux Docker 容器中安装 Microsoft Office,也无法将其部署到无服务器环境中。

C# 互操作会带来哪些问题?

互操作性方法存在一些重大挑战,使其不适用于现代DevOps实践和云原生部署。

不幸的是,Stack Overflow 和其他编程网站仍然推荐它,因为它们的思想还停留在 2000 年代初期的讨论帖中。

部署依赖项:需要在运行源代码的每台机器上安装 Microsoft Office,包括生产服务器。 这增加了许可成本和部署复杂性。

内存管理:必须使用 Marshal.ReleaseComObject() 显式释放 COM 对象。 即使遗漏一个对象也会导致Excel进程挂在内存中,如Stack Overflow上详细记录。 考虑数据透视缓存。

平台限制:此解决方案仅适用于已安装 Office 的 Windows 系统。 它可能运行速度极慢,并导致内存泄漏。 不支持LinuxmacOS 、Docker 容器或Azure Functions等云平台。 这严重限制了部署选项,并阻碍了现代容器编排平台的使用。

性能问题:启动 Excel 应用程序实例速度慢且资源消耗大,尤其是服务器端处理。

版本兼容性:不同 Office 版本可能具有不同的 COM 接口,从而导致跨环境的兼容性问题。

IronXL 如何在不使用互操作的情况下以编程方式创建数据透视表?

IronXL使用托管代码创建数据透视表,没有 COM 依赖项,其创建方式有所不同。 虽然它不能创建原生的 Excel 数据透视表,但它提供了强大的聚合功能,非常适合容器化部署和云原生架构。 该库的性能优化包括速度提升 40 倍,内存使用量从 19.5 GB 减少到 1 GB 以下,使其成为资源受限的容器环境的理想选择。

这种方法对于 XLSX 或 XLS 文件来说有何现代之处?

using IronXL;
using System.Linq;
using System.Data; // Keep this namespace
using static System.Data.DataTableExtensions; // Use 'using static' for DataTableExtensions
class Program
{
    static void Main(string[] args)
    {
        // Load Excel file - no Office required
        WorkBook workbook = WorkBook.Load("SalesData.xlsx");
        WorkSheet dataSheet = workbook.WorkSheets[0];
        // Convert to DataTable for powerful manipulation
        var dataTable = dataSheet.ToDataTable(true); // true = use first row as column headers
        // Create pivot-style aggregation using LINQ
        var pivotData = dataTable.AsEnumerable()
            .GroupBy(row => new {
                Product = row["Product"].ToString(),
                Region = row["Region"].ToString()
            }) //range
            .Select(g => new {
                Product = g.Key.Product,
                Region = g.Key.Region,
                TotalSales = g.Sum(row => Convert.ToDecimal(row["Sales"])),
                AverageSale = g.Average(row => Convert.ToDecimal(row["Sales"])),
                Count = g.Count()
            }); 
        // Create pivot report worksheet
        WorkSheet pivotSheet = workbook.CreateWorkSheet("PivotReport");
        // Build cross-tabulation structure
        var products = pivotData.Select(p => p.Product).Distinct().OrderBy(p => p);
        var regions = pivotData.Select(p => p.Region).Distinct().OrderBy(r => r);
        // Create headers
        pivotSheet["A1"].Value = "Product/Region";
        int col = 2;
        foreach (var region in regions)
        {
            pivotSheet[$"{(char)('A' + col - 1)}1"].Value = region; // string  
            col++;
        }
        // Populate pivot data
        int row = 2;
        foreach (var product in products)
        {
            pivotSheet[$"A{row}"].Value = product;
            col = 2;
            foreach (var region in regions)
            {
                var sales = pivotData
                    .Where(p => p.Product == product && p.Region == region)
                    .Select(p => p.TotalSales)
                    .FirstOrDefault();
                pivotSheet[$"{(char)('A' + col - 1)}{row}"].Value = sales;
                col++;
            }
            row++;
        }
        // Add totals using Excel formulas
        pivotSheet[$"A{row}"].Value = "Total"; // grand totals
        for (int c = 2; c <= regions.Count() + 1; c++)
        {
            pivotSheet[$"{(char)('A' + c - 1)}{row}"].Formula = $"=SUM({(char)('A' + c - 1)}2:{(char)('A' + c - 1)}{row - 1})";
        }
        // Proceeding to apply formatting
        var dataRange = pivotSheet[$"B2:{(char)('A' + regions.Count())}{row}"];
        dataRange.FormatString = "$#,##0.00";
        workbook.SaveAs("PivotReport.xlsx");
    }
}
using IronXL;
using System.Linq;
using System.Data; // Keep this namespace
using static System.Data.DataTableExtensions; // Use 'using static' for DataTableExtensions
class Program
{
    static void Main(string[] args)
    {
        // Load Excel file - no Office required
        WorkBook workbook = WorkBook.Load("SalesData.xlsx");
        WorkSheet dataSheet = workbook.WorkSheets[0];
        // Convert to DataTable for powerful manipulation
        var dataTable = dataSheet.ToDataTable(true); // true = use first row as column headers
        // Create pivot-style aggregation using LINQ
        var pivotData = dataTable.AsEnumerable()
            .GroupBy(row => new {
                Product = row["Product"].ToString(),
                Region = row["Region"].ToString()
            }) //range
            .Select(g => new {
                Product = g.Key.Product,
                Region = g.Key.Region,
                TotalSales = g.Sum(row => Convert.ToDecimal(row["Sales"])),
                AverageSale = g.Average(row => Convert.ToDecimal(row["Sales"])),
                Count = g.Count()
            }); 
        // Create pivot report worksheet
        WorkSheet pivotSheet = workbook.CreateWorkSheet("PivotReport");
        // Build cross-tabulation structure
        var products = pivotData.Select(p => p.Product).Distinct().OrderBy(p => p);
        var regions = pivotData.Select(p => p.Region).Distinct().OrderBy(r => r);
        // Create headers
        pivotSheet["A1"].Value = "Product/Region";
        int col = 2;
        foreach (var region in regions)
        {
            pivotSheet[$"{(char)('A' + col - 1)}1"].Value = region; // string  
            col++;
        }
        // Populate pivot data
        int row = 2;
        foreach (var product in products)
        {
            pivotSheet[$"A{row}"].Value = product;
            col = 2;
            foreach (var region in regions)
            {
                var sales = pivotData
                    .Where(p => p.Product == product && p.Region == region)
                    .Select(p => p.TotalSales)
                    .FirstOrDefault();
                pivotSheet[$"{(char)('A' + col - 1)}{row}"].Value = sales;
                col++;
            }
            row++;
        }
        // Add totals using Excel formulas
        pivotSheet[$"A{row}"].Value = "Total"; // grand totals
        for (int c = 2; c <= regions.Count() + 1; c++)
        {
            pivotSheet[$"{(char)('A' + c - 1)}{row}"].Formula = $"=SUM({(char)('A' + c - 1)}2:{(char)('A' + c - 1)}{row - 1})";
        }
        // Proceeding to apply formatting
        var dataRange = pivotSheet[$"B2:{(char)('A' + regions.Count())}{row}"];
        dataRange.FormatString = "$#,##0.00";
        workbook.SaveAs("PivotReport.xlsx");
    }
}
$vbLabelText   $csharpLabel

这就是以容器友好方式创建数据透视表的方法。 这种方法可以在 Docker 容器、Kubernetes pod 和无服务器函数中无缝运行,无需任何外部依赖项。 整个应用程序可以打包成一个轻量级容器镜像,在任何支持 .NET 的环境中运行。

数据透视表的输出结果是什么样子的?

对比原始Excel销售数据和生成的透视表,显示按地区汇总的产品销售额及总计

输出结果展示了 IronXL 如何将原始销售数据转换为结构化的透视报告,而无需安装 Excel,这使其非常适合 CI/CD 管道中的自动化报告。

如何使用IronXL公式创建动态摘要?

对于需要类似于数据透视表刷新功能的动态更新场景,IronXL 可以利用Excel 的内置公式。 这种方法更可取——您的数据将以更现代、更优雅的方式进行处理。 该代码易于理解和设置,无需联系技术支持或阅读手册。 这种方法在容器化环境中尤其有价值,因为在容器化环境中,你需要基于公式的计算并自动更新。

如何使基于公式的摘要自动更新?

// Load the workbook
WorkBook workbook = WorkBook.Load(inputPath);
// Rename the first worksheet so formulas reference correctly
WorkSheet dataSheet = workbook.WorkSheets[0];
dataSheet.Name = "DataSheet";
// Convert worksheet to DataTable
DataTable dataTable = dataSheet.ToDataTable(true);
// Create new summary worksheet
WorkSheet summarySheet = workbook.CreateWorkSheet("DynamicSummary");
// Get unique product-region combinations
var uniqueCombos = dataTable.AsEnumerable()
    .Select(row => new {
        Product = row["Product"].ToString(),
        Region = row["Region"].ToString()
    })
    .Distinct()
    .OrderBy(x => x.Product)
    .ThenBy(x => x.Region);
// Add header row
summarySheet["A1"].Value = "Product";
summarySheet["B1"].Value = "Region";
summarySheet["C1"].Value = "Total Sales";
summarySheet["D1"].Value = "Count";
// Populate rows with formulas
int rowIndex = 2;
foreach (var combo in uniqueCombos)
{
    summarySheet[$"A{rowIndex}"].Value = combo.Product;
    summarySheet[$"B{rowIndex}"].Value = combo.Region;
    // Adjust column references if your Sales column is C (not D)
    summarySheet[$"C{rowIndex}"].Formula =
        $"=SUMIFS(DataSheet!C:C,DataSheet!A:A,\"{combo.Product}\",DataSheet!B:B,\"{combo.Region}\")";
    summarySheet[$"D{rowIndex}"].Formula =
        $"=COUNTIFS(DataSheet!A:A,\"{combo.Product}\",DataSheet!B:B,\"{combo.Region}\")";
    rowIndex++;
}
// Optional: add total row
summarySheet[$"A{rowIndex}"].Value = "Total";
summarySheet[$"C{rowIndex}"].Formula = $"=SUM(C2:C{rowIndex - 1})";
summarySheet[$"D{rowIndex}"].Formula = $"=SUM(D2:D{rowIndex - 1})";
// Save output file
workbook.SaveAs(outputPath);  //filename
// Load the workbook
WorkBook workbook = WorkBook.Load(inputPath);
// Rename the first worksheet so formulas reference correctly
WorkSheet dataSheet = workbook.WorkSheets[0];
dataSheet.Name = "DataSheet";
// Convert worksheet to DataTable
DataTable dataTable = dataSheet.ToDataTable(true);
// Create new summary worksheet
WorkSheet summarySheet = workbook.CreateWorkSheet("DynamicSummary");
// Get unique product-region combinations
var uniqueCombos = dataTable.AsEnumerable()
    .Select(row => new {
        Product = row["Product"].ToString(),
        Region = row["Region"].ToString()
    })
    .Distinct()
    .OrderBy(x => x.Product)
    .ThenBy(x => x.Region);
// Add header row
summarySheet["A1"].Value = "Product";
summarySheet["B1"].Value = "Region";
summarySheet["C1"].Value = "Total Sales";
summarySheet["D1"].Value = "Count";
// Populate rows with formulas
int rowIndex = 2;
foreach (var combo in uniqueCombos)
{
    summarySheet[$"A{rowIndex}"].Value = combo.Product;
    summarySheet[$"B{rowIndex}"].Value = combo.Region;
    // Adjust column references if your Sales column is C (not D)
    summarySheet[$"C{rowIndex}"].Formula =
        $"=SUMIFS(DataSheet!C:C,DataSheet!A:A,\"{combo.Product}\",DataSheet!B:B,\"{combo.Region}\")";
    summarySheet[$"D{rowIndex}"].Formula =
        $"=COUNTIFS(DataSheet!A:A,\"{combo.Product}\",DataSheet!B:B,\"{combo.Region}\")";
    rowIndex++;
}
// Optional: add total row
summarySheet[$"A{rowIndex}"].Value = "Total";
summarySheet[$"C{rowIndex}"].Formula = $"=SUM(C2:C{rowIndex - 1})";
summarySheet[$"D{rowIndex}"].Formula = $"=SUM(D2:D{rowIndex - 1})";
// Save output file
workbook.SaveAs(outputPath);  //filename
$vbLabelText   $csharpLabel

这些公式与源数据保持实时连接,当数据表更改时自动更新 - 类似于数据透视表刷新行为,但没有Interop依赖。 这种方法非常适合需要生成动态报告而无需外部依赖项的容器化微服务。

动态摘要能带来哪些结果?

如果我们将这段代码应用到上一个示例中的示例 Excel 文件,我们将得到以下输出:

Excel电子表格显示产品销售数据,其中包含动态汇总公式,显示各个地区的产品(笔记本电脑、手机、平板电脑)及其计算出的总数和数量。

动态汇总方法提供实时计算,当源数据发生变化时会自动更新,因此非常适合容器化环境中的自动化报告管道。 这样就无需定期刷新数据透视表,并且可以在.NET MAUIBlazor应用程序中无缝运行。

如何比较 C# 互操作 和 IronXL 在数据透视表方面的性能?

方面

C# 互操作

IronXL

办公室要求

是的 - 完全安装

否 - 独立库

平台支持

仅限 Windows

Windows、Linux、macOS、Docker

内存管理

需要手动清理 COM 对象

自动 .NET 垃圾回收

部署

复杂 - 办公室许可

简单 - 单个 DLL

表现

Excel进程启动缓慢

快速内存计算

云端兼容

否 - Azure 限制

是的 - 支持 Azure Functions

原生透视表

无 - 聚合替代方案

发展速度

速度慢 - COM 复杂性

快速直观的 API

容器支持

不——无法将办公室容器化。

是的——已支持 Docker。

健康检查

困难 - COM 进程监控

简易型 - 标准 .NET 监控

DevOps角度来看,IronXL 的架构为现代部署场景提供了显著优势。 该库能够在没有外部依赖项的情况下在容器中运行,这意味着您可以创建轻量级的 Docker 镜像,从而快速部署并高效扩展。 健康检查可以使用标准的 .NET 模式来实现,该库的安全特性包括DigiCert认证和无 COM 接口,从而减少了攻击途径。

你应该选择哪种方法?

何时应该使用 C# 互操作?

选择C# 互操作当:

  • 绝对需要本机Excel数据透视表对象
  • 完全在 Windows 系统上工作,所有 Office 服务均已安装。
  • 仅部署到您管理的桌面系统
  • 现有的遗留代码依赖于Interop
  • 使用旧版 .NET Framework
  • 您目前没有容器化或迁移到云端的计划

IronXL 在什么情况下效果更佳?

选择IronXL当:

  • 部署到服务器或云环境(Azure、AWS)
  • 构建可在容器中运行的跨平台应用程序
  • 需要更高的性能,速度提升 40 倍
  • 避免 Office 许可费用和部署复杂性 需要更简单的代码,并具备自动许可证密钥管理功能
  • 支持 Mac、iOS、Android 和 Linux 系统
  • 熟悉现代 .NET Core 和 .NET 5-10 的开发。
  • 以编程方式配置数据透视表字段 构建可导出为各种格式的微服务
  • 在 CI/CD 流水线中实现自动化报告
  • 为容器编排创建健康检查端点 *不同电子表格格式之间的转换

我们在使用 C# 创建数据透视表方面学到了什么?

虽然 C# Excel Interop 可以创建原生透视表,但其部署限制和复杂性使其对于现代应用程序来说越来越不切实际,尤其是在容器化环境中。 IronXL通过数据聚合和基于公式的汇总提供强大的替代方案,消除了对 Office 的依赖,同时保持了分析能力。

对于正在寻找无需互操作即可开发透视表的替代方案的开发人员和DevOps工程师来说,IronXL 提供了一条更优的途径,避免了 COM 的复杂性,可在所有平台上运行,并简化部署。 虽然没有本机数据透视对象的权衡通过更大的灵活性、更好的性能和消除Office许可要求得以补偿。 对于DevOps团队而言,最重要的是 IronXL 能够实现真正的基础设施即代码,支持容器化部署、自动扩展以及与现代 CI/CD 管道的无缝集成。

该库的综合功能集包括条件格式设置单元格样式设置公式支持数据验证,使其成为现代 .NET 应用程序中 Excel 自动化的完整解决方案。 无论您是处理 CSV 文件管理工作表还是实施复杂的数据转换,IronXL 都提供了一个一致且易于部署的 API。

立即开始使用 IronXL。
green arrow pointer

准备好升级您的 Excel 自动化流程,并使用现代 C# 创建数据透视表代码了吗?

通过 NuGet 包管理器,您可以在几秒钟内将 IronXL 集成到您的 C# 应用程序中。 试用免费试用版,消除生产应用程序中的互操作依赖关系,简化容器部署。

常见问题解答

Excel中的数据透视表是什么?

Excel中的数据透视表是一种强大的工具,用于汇总、分析、探索和展示数据。它允许用户将列转换为行,反之亦然,从而实现动态数据分析。

为什么在C#中使用IronXL来创建Excel数据透视表?

IronXL允许开发者在C#中创建Excel数据透视表,而无需依赖Office Interop,消除了对Excel安装的需求并减少了依赖,使其成为现代且高效的选择。

IronXL与C# Interop在Excel操作方面如何比较?

IronXL提供了一个更简化且独立的方法,而不像C# Interop需要Office安装。IronXL简化了数据透视表和其他Excel操作的创建,而没有Interop的复杂性。

没有安装Excel,我可以生成数据透视表吗?

是的,使用IronXL,您可以在没有安装Excel的情况下在C#应用程序中生成数据透视表,因为它独立于Microsoft Office运行。

IronXL适合处理大数据集吗?

IronXL设计用于高效处理大数据集,使其适合需要稳健数据操作和数据透视表生成的应用程序。

使用IronXL相对于传统方法有哪些优势?

IronXL提供了一种现代、无依赖的替代传统方法如C# Interop,提供了易用性、灵活性,并支持复杂数据操作而无需Excel安装。

我需要学习VBA来使用IronXL进行数据透视表吗?

不需要,IronXL允许开发者直接在C#中创建和管理数据透视表,消除了学习VBA或其他Excel特定编程语言的需求。

Curtis Chau
技术作家

Curtis Chau 拥有卡尔顿大学的计算机科学学士学位,专注于前端开发,精通 Node.js、TypeScript、JavaScript 和 React。他热衷于打造直观且美观的用户界面,喜欢使用现代框架并创建结构良好、视觉吸引力强的手册。

除了开发之外,Curtis 对物联网 (IoT) 有浓厚的兴趣,探索将硬件和软件集成的新方法。在空闲时间,他喜欢玩游戏和构建 Discord 机器人,将他对技术的热爱与创造力相结合。