G#(GSharp)正在尝试解决一个长期存在的矛盾:开发者希望获得更简洁、更现代的语言体验,却不愿放弃 CLR、.NET 类库和现有工程资产。根据微软软件工程师 David Obando 的介绍,G# 的目标是一门现代、简单、易用的 .NET 编程语言,并从 Go、Kotlin 和 Swift 的设计理念中吸收经验。
现在公开信息仍然有限,因此不宜过早把某项具体语法、工具链能力或发布时间当作既定事实。现阶段更值得讨论的是它选择的方向:语言表面可以重新设计,运行时和生态则继续建立在熟悉的 .NET 之上。
新语言,为什么仍然选择 CLR
一门新语言最昂贵的部分往往不是语法解析器,而是语言之外的工程体系:垃圾回收、异常处理、调试器、包管理、构建工具、跨平台运行时以及大量基础库。
G# 将运行环境放在 .NET 上,意味着它的价值不必从重新实现这些能力开始。理想情况下,开发团队可以继续使用熟悉的基础设施:
- CLR 负责执行托管代码和内存管理。
- .NET 基础类库提供集合、网络、文件和并发等能力。
- NuGet 继续承担依赖分发。
- 既有 C#、F# 或其他 CLR 程序集可以成为互操作对象。
- CI/CD、容器镜像和可观测性体系不必全部推倒重建。
不过,这些是基于“运行在熟悉的 .NET 环境中”所推导出的评估方向,不代表 G# 当前已经完整支持所有互操作和工具链场景。判断一门 CLR 新语言是否能进入生产环境,关键不只是它能否生成 IL,还要看调试信息、泛型映射、异常边界、异步模型和 NuGet 依赖处理是否稳定。
Go、Kotlin 和 Swift 的影响应当怎样理解
“融合三种语言理念”不等于把三套语法拼在一起。更合理的理解是,G# 希望在几个维度上取得平衡。
Go 的吸引力通常来自较小的语言表面、清晰的代码结构和容易预测的行为。Kotlin 展示了如何在成熟虚拟机生态上改善开发体验,并与既有代码共存。Swift 则代表了现代静态类型语言对可读性、类型表达和开发者体验的持续打磨。
对 G# 来说,真正困难的是控制复杂度。如果为了覆盖所有语言习惯而不断增加语法糖,所谓“简单且可预测”很快就会失去意义。反过来,如果语言过度精简,又可能无法自然表达 .NET 中广泛使用的泛型、属性、异步操作和面向对象 API。
因此,评估 G# 时可以重点观察以下问题:
- 同一段逻辑是否只有少数几种惯用写法。
- 空值、错误和异步操作是否在代码中清晰可见。
- 调用现有 .NET API 时是否需要大量适配代码。
- 编译错误能否准确指出类型和互操作问题。
- 语言版本升级是否保持源代码兼容性。
在没有更完整规范之前,不应根据“受到 Kotlin 或 Swift 启发”就推断 G# 已经采用某种特定的空安全、模式匹配或并发机制。这些都需要以实际编译器和语言文档为准。
可以这样实践:先建立 CLR 互操作验收项目
目前不必等待 G# 工具链完全成熟,团队就可以先定义验收边界。下面这个可直接运行的 .NET 8 示例建立了一个类库和一个控制台程序,用来验证“新语言生成的程序集能否被现有 .NET 项目调用”这一核心场景。
运行前需要安装 .NET 8 SDK。以下命令会创建一个临时目录,并写入完整的 C# 基线项目:
mkdir -p gsharp-evaluation
cd gsharp-evaluation
dotnet new sln -n GSharpEvaluation
dotnet new classlib -n SharedContracts -f net8.0
dotnet new console -n ExistingConsumer -f net8.0
dotnet sln add SharedContracts/SharedContracts.csproj
dotnet sln add ExistingConsumer/ExistingConsumer.csproj
dotnet add ExistingConsumer/ExistingConsumer.csproj reference SharedContracts/SharedContracts.csproj
cat > SharedContracts/GreetingService.cs <<'EOF'
namespace SharedContracts;
public sealed record Greeting(string Message, DateTimeOffset CreatedAt);
public static class GreetingService
{
public static Greeting Create(string name)
{
ArgumentException.ThrowIfNullOrWhiteSpace(name);
return new Greeting($"Hello, {name}", DateTimeOffset.UtcNow);
}
}
EOF
cat > ExistingConsumer/Program.cs <<'EOF'
using SharedContracts;
var greeting = GreetingService.Create("G# evaluator");
Console.WriteLine($"{greeting.Message} @ {greeting.CreatedAt:O}");
EOF
dotnet run --project ExistingConsumer/ExistingConsumer.csproj
输出应包含问候语和 UTC 时间。等 G# 编译器可用于试验后,可以这样改造:保留 ExistingConsumer,将 SharedContracts 的实现替换成 G# 项目,同时维持相同的公开类型和方法。这样测试的不是演示语法,而是几个更实际的问题:
- C# 是否能直接引用 G# 生成的程序集。
- 字符串、时间、记录类型和异常能否正确跨越语言边界。
- IDE 能否跳转到符号并显示合理的类型信息。
- Release 构建、测试覆盖率和堆栈跟踪是否正常。
还可以用以下命令记录评估机器的 SDK 与运行时版本,避免把环境差异误判成语言缺陷:
dotnet --info
dotnet --list-sdks
dotnet --list-runtimes
如果 G# 当前提供的项目格式、命令名称或目标框架与示例不同,应以其实际工具链为准;上面的项目是互操作测试基线,不是对 G# 语法和 CLI 的声明。
采用前要看的不只是语法
新语言原型很容易在几十行示例中显得清爽,生产系统却会暴露另一组问题。团队进行技术预研时,应当把语言设计和工程成熟度分开打分。
语言层面需要检查可读性、类型系统、错误处理、并发模型和版本兼容策略。工程层面则要验证增量编译、测试框架、调试器、格式化工具、静态分析、包发布和构建缓存。互操作层面还应覆盖泛型、可空类型、异步调用、反射、特性以及异常传播。
G# 最有吸引力的前景,并不是成为“另一种 C#”,而是在保留 .NET 运行时价值的同时,提供更小、更一致的语言表面。它面临的风险也同样明确:生态规模有限、工具链尚未成熟,以及与既有 .NET API 设计之间可能出现摩擦。
现阶段适合把 G# 放进技术雷达,使用隔离的实验项目跟踪编译器、规范和互操作进展;不适合仅凭语法示例就承担核心生产负载。等它能够稳定通过真实项目中的构建、调试、测试、发布和升级流程,再讨论大规模采用,会比单纯比较语言关键字更有意义。