从Barcoder迁移到IronBarcode
本指南涵盖了.NET开发人员从 条形码 到IronBarcode的完整迁移路径。 此次迁移将两个软件包合并为一个,用单一方法调用替换了编码器-渲染器管道,并将所有特定格式的命名空间导入——using Barcoder.Code128, using Barcoder.Qr, using IronBarCode;。 ImageRenderer / ImageRendererOptions类型已被完全移除。 如果读取功能缺失导致您在Barcoder旁边添加了第二个库,IronBarcode本地支持读取功能,带有BarcodeReader.Read()。
为什么要从条形码迁移?
从 条形码 迁移的团队报告了以下触发因素:
**新增读取要求:**条形码库仅用于生成条形码,不具备解码功能。 当应用程序需要验证传入的条形码、处理扫描的文档或读取上传的 PDF 中的条形码时,Barcoder 无法提供任何解决方案。 添加第二个读取库会创建一个拆分的代码库,其中包含两个NuGet依赖项、两个需要维护的 API 和两个需要跟踪的版本历史记录。 IronBarcode通过同一个软件包处理条码生成和读取操作。
.NET Framework兼容性中断:Barcoder.Renderer.Image已放弃对.NET Framework的支持。 在.NET Framework 4.x 上运行服务或桌面应用程序的团队,如果在例行依赖项维护期间更新图像渲染器包,则会遇到生成失败。 IronBarcode支持.NET Framework 4.6.2 至.NET 9,无需针对不同目标进行条件打包。
包版本漂移:Barcoder.Renderer.Image是独立版本的NuGet包。 在依赖关系刷新期间,如果只更新其中一个而不更新另一个,可能会导致不兼容。 在拥有多个项目的存储库中,确保每个项目中每个渲染器的版本一致是一项协调负担,而且随着团队规模的扩大,这种负担也会增加。
**添加多种格式:**一个项目如果从 Code128 开始,后来添加了 QR,然后又添加了 DataMatrix,则必须添加一个新的命名空间导入,并为每种格式学习不同的编码器类及其不同的方法参数。 IronBarcode使用BarcodeEncoding枚举——添加一种格式只需对现有调用进行一词更改。 没有新的命名空间,没有新的类,也没有不同的方法签名。
MAUI 和云部署要求: 条形码 没有提供对 MAUI、Docker、AWS Lambda 或 Azure 部署的支持文档。 构建跨平台移动应用程序或无服务器条形码处理管道的团队发现,Barcoder 文档中记录的目标并未涵盖这些场景。
基本问题
条形码将格式选择与命名空间导入相结合,并强制每个输出操作使用渲染器管道。 切换格式或输出类型需要对代码进行结构性更改:
// Barcoder: 3 namespaces, encoder-specific class, renderer pipeline, stream management
using Barcoder;
using Barcoder.Code128;
using Barcoder.Renderers;
IBarcode barcode = Code128Encoder.Encode("PRODUCT-12345", false);
var renderer = new ImageRenderer(new ImageRendererOptions
{
ImageFormat = ImageFormat.Png,
PixelSize = 2,
BarHeightFor1DBarcode = 50
});
using var stream = File.OpenWrite("barcode.png");
renderer.Render(barcode, stream);Imports Barcoder
Imports Barcoder.Code128
Imports Barcoder.Renderers
Imports System.IO
Dim barcode As IBarcode = Code128Encoder.Encode("PRODUCT-12345", False)
Dim renderer = New ImageRenderer(New ImageRendererOptions With {
.ImageFormat = ImageFormat.Png,
.PixelSize = 2,
.BarHeightFor1DBarcode = 50
})
Using stream As FileStream = File.OpenWrite("barcode.png")
renderer.Render(barcode, stream)
End UsingIronBarcode用一个链式调用就能表达相同的操作:
// IronBarcode: 1 namespace, 1 method, 1 output call
using IronBarCode;
BarcodeWriter.CreateBarcode("PRODUCT-12345", BarcodeEncoding.Code128)
.SaveAsPng("barcode.png");Imports IronBarCode
BarcodeWriter.CreateBarcode("PRODUCT-12345", BarcodeEncoding.Code128) _
.SaveAsPng("barcode.png")IronBarcode与 Barcoder:功能对比
| 特征 | 条形码 | IronBarcode |
|---|---|---|
| 需要NuGet包 | 最少 2 个(条形码器 + 条形码.渲染器.图像) | 1(IronBarcode) |
| 条形码生成 | 是 | 是 |
| 条形码读取/解码 | 否 | 是 |
| 按格式划分的编码器类 | 是的——Code128Encoder, QrEncoder, DataMatrixEncoder等。 | 否——BarcodeEncoding枚举 |
| 按格式命名空间导入 | 是的——Barcoder.Code128, Barcoder.Qr, Barcoder.DataMatrix | 否——仅using IronBarCode |
| 结果对象的输出方法 | 否——IBarcode没有保存方法 | 是的——SaveAsPng, ToPngBinaryData, ToStream |
| PDF阅读 | 否 | 是的——BarcodeReader.Read(path) |
| 带有徽标的二维码 | 否 | 是的——.AddBrandLogo(path) |
| .NET Framework支持 | 图像渲染器中已删除 | .NET Framework 4.6.2+ |
| .NET 9 支持 | 活动不明/有限 | 是 |
| MAUI 支持 | 否 | 是的——iOS、Android、Windows、macOS |
| Docker / Azure / AWS Lambda | 未记录。 | 是 |
| 许可 | 麻省理工学院(开源) | 商业——Lite $999, Plus $1,499, Professional $2,399, Unlimited $4,799 |
| 阅读速度控制 | 否 | 是的——ReadingSpeed枚举 |
| 多条形码检测 | 否 | 是的——ExpectMultipleBarcodes选项 |
快速入门:条形码到IronBarcode 的迁移
步骤 1:替换 NuGet 软件包
从项目中移除所有条形码软件包:
dotnet remove package Barcoder
dotnet remove package Barcoder.Renderer.Image
如果您还添加了 SVG 渲染器:
dotnet remove package Barcoder.Renderer.Svg
安装IronBarcode:
dotnet add package IronBarcode
步骤 2:更新命名空间
从项目中的每个文件中删除所有using Barcoder.*语句:
// Remove all of these
using Barcoder;
using Barcoder.Code128;
using Barcoder.Qr;
using Barcoder.DataMatrix;
using Barcoder.Renderers;
using Barcoder.Ean;
using Barcoder.Pdf417;
// ... and any other Barcoder.* namespaces in your projectnet将它们全部替换为一条 using 指令:
using IronBarCode;Imports IronBarCode步骤 3:初始化许可证
在应用程序启动时添加许可证初始化——Program.cs, Startup.cs, 或MauiProgram.cs:
IronBarCode.License.LicenseKey = "YOUR-LICENSE-KEY";Imports IronBarCode
IronBarCode.License.LicenseKey = "YOUR-LICENSE-KEY"您可以在ironsoftware.com获取免费试用密钥。
代码迁移示例
Code128生成
条形码方法:
using Barcoder;
using Barcoder.Code128;
using Barcoder.Renderers;
IBarcode barcode = Code128Encoder.Encode("PRODUCT-12345", false);
var renderer = new ImageRenderer(new ImageRendererOptions
{
ImageFormat = ImageFormat.Png,
PixelSize = 2,
BarHeightFor1DBarcode = 50
});
using var stream = File.OpenWrite("barcode.png");
renderer.Render(barcode, stream);Imports Barcoder
Imports Barcoder.Code128
Imports Barcoder.Renderers
Imports System.IO
Dim barcode As IBarcode = Code128Encoder.Encode("PRODUCT-12345", False)
Dim renderer = New ImageRenderer(New ImageRendererOptions With {
.ImageFormat = ImageFormat.Png,
.PixelSize = 2,
.BarHeightFor1DBarcode = 50
})
Using stream As FileStream = File.OpenWrite("barcode.png")
renderer.Render(barcode, stream)
End UsingIronBarcode方法:
// NuGet: dotnet add package IronBarcode
using IronBarCode;
BarcodeWriter.CreateBarcode("PRODUCT-12345", BarcodeEncoding.Code128)
.SaveAsPng("barcode.png");Imports IronBarCode
BarcodeWriter.CreateBarcode("PRODUCT-12345", BarcodeEncoding.Code128) _
.SaveAsPng("barcode.png")Code128Encoder, ImageRenderer, ImageRendererOptions以及流管理功能已完全移除。 格式在统一的BarcodeEncoding.Code128。 输出方法直接存在于返回的GeneratedBarcode对象上。 IronBarcode条形码生成文档涵盖样式、边距和颜色自定义。
二维码生成
条形码方法:
using Barcoder;
using Barcoder.Qr;
using Barcoder.Renderers;
IBarcode barcode = QrEncoder.Encode("https://example.com", ErrorCorrectionLevel.M);
var renderer = new ImageRenderer(new ImageRendererOptions
{
ImageFormat = ImageFormat.Png,
PixelSize = 4
});
using var stream = File.OpenWrite("qr.png");
renderer.Render(barcode, stream);Imports Barcoder
Imports Barcoder.Qr
Imports Barcoder.Renderers
Dim barcode As IBarcode = QrEncoder.Encode("https://example.com", ErrorCorrectionLevel.M)
Dim renderer = New ImageRenderer(New ImageRendererOptions With {
.ImageFormat = ImageFormat.Png,
.PixelSize = 4
})
Using stream = File.OpenWrite("qr.png")
renderer.Render(barcode, stream)
End UsingIronBarcode方法:
using IronBarCode;
QRCodeWriter.CreateQrCode("https://example.com", 500)
.SaveAsPng("qr.png");Imports IronBarCode
QRCodeWriter.CreateQrCode("https://example.com", 500) _
.SaveAsPng("qr.png")CreateQrCode的第二个参数是以像素为单位的输出尺寸。 错误纠正级别默认值为合理值,如有需要可进行配置。 IronBarcode还支持 条形码 无法实现的操作——在二维码中心嵌入品牌标识:
using IronBarCode;
QRCodeWriter.CreateQrCode("https://example.com", 500)
.AddBrandLogo("logo.png")
.SaveAsPng("qr-branded.png");Imports IronBarCode
QRCodeWriter.CreateQrCode("https://example.com", 500) _
.AddBrandLogo("logo.png") _
.SaveAsPng("qr-branded.png")数据矩阵生成
条形码方法:
using Barcoder;
using Barcoder.DataMatrix;
using Barcoder.Renderers;
IBarcode barcode = DataMatrixEncoder.Encode("ITEM-XYZ-001");
var renderer = new ImageRenderer(new ImageRendererOptions
{
ImageFormat = ImageFormat.Png,
PixelSize = 5
});
using var stream = File.OpenWrite("datamatrix.png");
renderer.Render(barcode, stream);Imports Barcoder
Imports Barcoder.DataMatrix
Imports Barcoder.Renderers
Imports System.IO
Dim barcode As IBarcode = DataMatrixEncoder.Encode("ITEM-XYZ-001")
Dim renderer = New ImageRenderer(New ImageRendererOptions With {
.ImageFormat = ImageFormat.Png,
.PixelSize = 5
})
Using stream As FileStream = File.OpenWrite("datamatrix.png")
renderer.Render(barcode, stream)
End UsingIronBarcode方法:
using IronBarCode;
BarcodeWriter.CreateBarcode("ITEM-XYZ-001", BarcodeEncoding.DataMatrix)
.SaveAsPng("datamatrix.png");Imports IronBarCode
BarcodeWriter.CreateBarcode("ITEM-XYZ-001", BarcodeEncoding.DataMatrix) _
.SaveAsPng("datamatrix.png")模式与Code128相同——仅更改BarcodeEncoding值。 没有新的命名空间导入,没有不同的类,也没有不同的方法签名。
控制输出尺寸
Barcoder使用BarHeightFor1DBarcode用于1D高度——没有直接的宽度/高度API。
条形码方法:
var renderer = new ImageRenderer(new ImageRendererOptions
{
ImageFormat = ImageFormat.Png,
PixelSize = 3,
BarHeightFor1DBarcode = 80
});
using var stream = File.OpenWrite("barcode.png");
renderer.Render(barcode, stream);Imports System.IO
Dim renderer = New ImageRenderer(New ImageRendererOptions With {
.ImageFormat = ImageFormat.Png,
.PixelSize = 3,
.BarHeightFor1DBarcode = 80
})
Using stream As FileStream = File.OpenWrite("barcode.png")
renderer.Render(barcode, stream)
End UsingIronBarcode方法:
using IronBarCode;
BarcodeWriter.CreateBarcode("PRODUCT-12345", BarcodeEncoding.Code128)
.ResizeTo(400, 100)
.SaveAsPng("barcode.png");Imports IronBarCode
BarcodeWriter.CreateBarcode("PRODUCT-12345", BarcodeEncoding.Code128) _
.ResizeTo(400, 100) _
.SaveAsPng("barcode.png").ResizeTo(width, height)需要显式像素尺寸。 它与同一GeneratedBarcode对象上的其他输出方法链式连接。
获取二进制数据而非文件
条形码方法:
using Barcoder;
using Barcoder.Code128;
using Barcoder.Renderers;
IBarcode barcode = Code128Encoder.Encode("PRODUCT-12345", false);
var renderer = new ImageRenderer(new ImageRendererOptions
{
ImageFormat = ImageFormat.Png,
PixelSize = 2
});
using var ms = new MemoryStream();
renderer.Render(barcode, ms);
byte[] pngBytes = ms.ToArray();Imports Barcoder
Imports Barcoder.Code128
Imports Barcoder.Renderers
Imports System.IO
Dim barcode As IBarcode = Code128Encoder.Encode("PRODUCT-12345", False)
Dim renderer = New ImageRenderer(New ImageRendererOptions With {
.ImageFormat = ImageFormat.Png,
.PixelSize = 2
})
Using ms As New MemoryStream()
renderer.Render(barcode, ms)
Dim pngBytes As Byte() = ms.ToArray()
End UsingIronBarcode方法:
using IronBarCode;
byte[] pngBytes = BarcodeWriter.CreateBarcode("PRODUCT-12345", BarcodeEncoding.Code128)
.ToPngBinaryData();Imports IronBarCode
Dim pngBytes As Byte() = BarcodeWriter.CreateBarcode("PRODUCT-12345", BarcodeEncoding.Code128).ToPngBinaryData()GeneratedBarcode上的链式方法调用替代。
条形码读取(新增功能)
条形码器没有读取 API。 如果您之前添加了第二个图书馆用于阅读,则可以将其删除。 IronBarcode使用与生成时相同的软件包,从文件、字节数组、流和 PDF 中读取数据。
IronBarcode方法:
using IronBarCode;
// Read from image file
var results = BarcodeReader.Read("barcode.png");
foreach (var result in results)
{
Console.WriteLine($"Value: {result.Value}");
Console.WriteLine($"Format: {result.Format}");
}
// Read from PDF natively — no image extraction step, no extra dependency
var pdfResults = BarcodeReader.Read("invoice.pdf");
// Read multiple barcodes from one image
var options = new BarcodeReaderOptions
{
Speed = ReadingSpeed.Balanced,
ExpectMultipleBarcodes = true
};
var multiResults = BarcodeReader.Read("warehouse-sheet.png", options);
foreach (var result in multiResults)
Console.WriteLine(result.Value);
// Read from a stream directly
using var fileStream = File.OpenRead("barcode.png");
var streamResults = BarcodeReader.Read(fileStream);Imports IronBarCode
' Read from image file
Dim results = BarcodeReader.Read("barcode.png")
For Each result In results
Console.WriteLine($"Value: {result.Value}")
Console.WriteLine($"Format: {result.Format}")
Next
' Read from PDF natively — no image extraction step, no extra dependency
Dim pdfResults = BarcodeReader.Read("invoice.pdf")
' Read multiple barcodes from one image
Dim options As New BarcodeReaderOptions With {
.Speed = ReadingSpeed.Balanced,
.ExpectMultipleBarcodes = True
}
Dim multiResults = BarcodeReader.Read("warehouse-sheet.png", options)
For Each result In multiResults
Console.WriteLine(result.Value)
Next
' Read from a stream directly
Using fileStream = File.OpenRead("barcode.png")
Dim streamResults = BarcodeReader.Read(fileStream)
End UsingIronBarcode条形码读取文档涵盖多页 PDF、感兴趣区域读取和性能调优选项。
SVG 输出
条形码方法:
using Barcoder.Renderers;
var svgRenderer = new SvgRenderer();
using var stream = File.OpenWrite("barcode.svg");
svgRenderer.Render(barcode, stream);Imports Barcoder.Renderers
Dim svgRenderer = New SvgRenderer()
Using stream = File.OpenWrite("barcode.svg")
svgRenderer.Render(barcode, stream)
End UsingIronBarcode方法:
using IronBarCode;
BarcodeWriter.CreateBarcode("data", BarcodeEncoding.Code128)
.SaveAsSvg("barcode.svg");Imports IronBarCode
BarcodeWriter.CreateBarcode("data", BarcodeEncoding.Code128) _
.SaveAsSvg("barcode.svg")一旦所有SVG渲染站点迁移,Barcoder.Renderer.Svg包可以移除。
条形码 API 到IronBarcode映射参考
| 条形码 | IronBarcode |
|---|---|
Code128Encoder.Encode("data", false) | BarcodeWriter.CreateBarcode("data", BarcodeEncoding.Code128) |
QrEncoder.Encode("data", ErrorCorrectionLevel.M) | QRCodeWriter.CreateQrCode("data", 500) |
DataMatrixEncoder.Encode("data") | BarcodeWriter.CreateBarcode("data", BarcodeEncoding.DataMatrix) |
new ImageRenderer(new ImageRendererOptions { ... }) | 不需要 |
renderer.Render(barcode, fileStream) | .SaveAsPng(path) |
renderer.Render(barcode, memoryStream) + ms.ToArray() | .ToPngBinaryData() |
IBarcode | GeneratedBarcode |
PixelSize = 2, BarHeightFor1DBarcode = 50 | .ResizeTo(width, height) |
SvgRenderer + renderer.Render(barcode, stream) | .SaveAsSvg(path) |
| 无读取 API | BarcodeReader.Read(path / stream / bytes / pdf) |
2个包——Barcoder + Barcoder.Renderer.Image | 1个包——IronBarcode |
按编码器格式的命名空间(Barcoder.Code128, Barcoder.Qr, 等) | BarcodeEncoding枚举,单一命名空间 |
常见迁移问题和解决方案
问题 1:IBarcode 没有保存方法
**Barcoder:**存储IBarcode变量并将其传递给渲染器的代码遵循延迟渲染模式。 任何接受renderer.Render()的辅助方法都必须重构。
**解决方案:**将GeneratedBarcode。 GeneratedBarcode对象携带其输出方法——将其传递给需要保存或序列化结果的任何方法:
//IronBarcode— GeneratedBarcode carries output methods
using IronBarCode;
var barcode = BarcodeWriter.CreateBarcode("data", BarcodeEncoding.Code128);
SaveBarcodeToFile(barcode, "output.png");
static void SaveBarcodeToFile(GeneratedBarcode barcode, string path)
{
barcode.SaveAsPng(path);
}
在解决方案中搜索renderer.Render(可以定位到所有延迟渲染站点。
问题二:PixelSize 没有直接的等效值
Barcoder:PixelSize是条形码自然模块大小的比例乘数。输出尺寸取决于内容长度、格式和乘数的组合。 BarHeightFor1DBarcode独立控制1D高度。 没有直接的宽度/高度规格。
**解决方案:**测量现有Barcoder代码产生的实际像素输出,并将这些值传递给.ResizeTo(width, height):
BarcodeWriter.CreateBarcode("data", BarcodeEncoding.Code128)
.ResizeTo(300, 80)
.SaveAsPng("barcode.png");BarcodeWriter.CreateBarcode("data", BarcodeEncoding.Code128) _
.ResizeTo(300, 80) _
.SaveAsPng("barcode.png")问题 3:格式化命名空间导入累积
**Barcoder:**项目中使用的每种条形码格式都会向使用它的每个文件添加一个using Barcoder.[Format]指令。 使用Code128, QR和DataMatrix的项目通常每个文件有三个特定格式的导入以及using Barcoder.Renderers。
**解决方案:**搜索所有using IronBarCode;替换所有语句:
grep -rn "using Barcoder" --include="*.cs" .
每个结果都是一行需要移除的行,用单一的using IronBarCode;指令替换,每个文件一次。
问题 4:软件包版本同步错误
**Barcoder:**在只更新Barcoder.Renderer.Image之一后进行构建可能会产生类型解析错误,因为这两个包在不同版本组合中可能不兼容。
**解决方案:**在移除两个Barcoder包并安装IronBarcode后,只有一个包和一个版本。 此类软件包锁定冲突将被消除。 验证Barcoder.*引用。
条码迁移检查清单
迁移前任务
在进行任何更改之前,请先审核您的代码库,找出所有条形码的使用情况:
grep -rn "using Barcoder" --include="*.cs" .
grep -rn "Code128Encoder\.Encode" --include="*.cs" .
grep -rn "QrEncoder\.Encode" --include="*.cs" .
grep -rn "DataMatrixEncoder\.Encode" --include="*.cs" .
grep -rn "ImageRenderer\|ImageRendererOptions" --include="*.cs" .
grep -rn "SvgRenderer" --include="*.cs" .
grep -rn "renderer\.Render(" --include="*.cs" .
grep -rn "IBarcode" --include="*.cs" .
grep -rn "ErrorCorrectionLevel" --include="*.cs" .
记录所有IBarcode存储为变量类型或作为方法参数传递的位置——这些是需要最多注意的延迟渲染站点。 记录所有BarHeightFor1DBarcode值并测量相应的输出尺寸。
代码更新任务
- 从所有项目中移除
BarcoderNuGet包 - 从所有项目中移除
Barcoder.Renderer.ImageNuGet包 - 如果存在则移除
Barcoder.Renderer.SvgNuGet包 - 安装
IronBarcodeNuGet包 - 在应用程序启动时添加
IronBarCode.License.LicenseKey = "YOUR-LICENSE-KEY"; - 将所有
using IronBarCode; - 用
Code128Encoder.Encode(...) - 用
QrEncoder.Encode(...) - 用
DataMatrixEncoder.Encode(...) - 移除所有
new ImageRenderer(new ImageRendererOptions {...})构造 - 在
renderer.Render(barcode, fileStream) - 用
renderer.Render(barcode, memoryStream)+ms.ToArray() - 用
SvgRenderer的使用 - 将
GeneratedBarcode - 用
PixelSize/BarHeightFor1DBarcode选项
迁移后测试
- 使用标准扫描器或IronBarcode自有的阅读器,验证每个生成的条形码格式是否都能正确扫描。
- 将视觉输出尺寸与迁移前的输出进行比较——确认
.ResizeTo()值产生了等效的尺寸 - 使用手机扫描器测试二维码输出,以确认纠错级别是否足够。 如果您的项目使用了 SVG,请验证 SVG 输出在浏览器和打印环境中是否能正确渲染。
- 如果您移除了第二个读取库,验证
BarcodeReader.Read()对相同输入产生了等效的解码值 - 测试 PDF 读取功能,确保你的项目能够处理 PDF 文档——确认多页 PDF 文档的所有页面都能正确读取。
- 在没有剩余
Barcoder.*引用的情况下进行构建——编译错误表示遗漏的迁移站点
迁移到IronBarcode的主要优势
**统一的包依赖关系:**一个NuGet包可以替代两个(如果还使用了 SVG 渲染器,则替代三个)。 只需跟踪一个版本,监控一个变更日志,并且在依赖项更新期间不会出现跨软件包兼容性问题。 packages.lock.json复杂性立即降低。
**原生读取功能:**以前需要第二个库才能读取条形码的应用程序可以整合到IronBarcode上。 BarcodeReader.Read()通过相同的API接受图像文件、字节数组、流和PDF,内置了多条形码检测和读取速度调优。
**格式选择作为配置:**向现有项目中添加新的条形码格式只需对现有BarcodeEncoding枚举值进行一词更改。 没有新的命名空间导入,没有新的编码器类,也没有新的包。 这样一来,随着产品需求的演变,格式扩展的阻力就会很小。
显式输出尺寸:PixelSize乘数系统。 代码与输出之间的关系是直接且可预测的,与条形码内容长度或特定格式的模块大小无关。
扩展的平台覆盖范围: IronBarcode支持.NET Framework 4.6.2 至.NET 9,MAUI 在 iOS、Android、Windows 和 macOS 上的部署,并记录了部署到 Docker、AWS Lambda 和 Azure Functions 的过程。 超出.NET Core的团队平台需求扩展将不会遇到影响Barcoder.Renderer.Image的框架支持差距。
积极维护和未来兼容性: IronBarcode会定期发布更新,并记录.NET兼容性时间表。 活跃的发布节奏使库与当前.NET版本保持一致,而这并不是从最近发布活动有限的库中可以保证的。

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