DSL on C#: 一晚创建自己的编程语言

面向初学者的在线Python训练器

轻松学习Python,无需理论负担。在浏览器中直接解决带自动检查的实践任务,无需安装任何东西。

开始课程

引言:为什么你的代码应该用业务语言说话

\n\n

想象一下:你正在为一个复杂的交易机器人编写配置。无需数百行if-else和魔法数字,你只需编写:BuyWhen(Price.CrossesAbove(MA(50)))。你的经理不懂C#,但看到这段代码就能理解逻辑。这不是魔法——这是C#中的DSL。

\n\n

领域特定语言(Domain-Specific Languages, DSL)不仅仅是一种流行趋势。它是一种工具,能将混乱的技术细节转化为优雅、可读的代码。在本文中,我们不仅会讨论理论——我们将从零开始创建一个真实的C# DSL,分析常见错误,并展示如何将这一实践立即应用到你的项目中。

\n\n

什么是DSL,为什么开发者需要它?

\n\n

DSL是一种为特定任务创建的编程语言。与通用语言(C#、Java、Python)不同,DSL使用领域语言说话。在C#中有两种主要方法:

\n\n
    \n
  • 内部DSL——通过Fluent API和表达式树构建在C#之上。你仍处于原生环境中。
  • \n
  • 外部DSL——需要解析(通常通过Roslyn)并生成C#代码。
  • \n
\n\n

DSL的实际好处:降低认知负荷,减少错误数量(业务逻辑与技术实现分离),并加速新开发者的入职。

\n\n

在C#中创建内部DSL:逐步指南

\n\n

步骤1:定义领域和语法

\n\n

假设我们正在创建一个用于数据验证的DSL。不再使用:

\n\n
if (user.Age < 18 || user.Name == null || user.Email == null)\n    throw new ValidationException();
\n\n

我们希望这样编写:

\n\n
Validate.User(user)\n    .MustBeAdult()\n    .MustHaveName()\n    .MustHaveEmail();
\n\n

步骤2:Fluent接口——C# DSL的基础

\n\n

关键元素是从每个方法返回this。创建一个验证器类:

\n\n
public class UserValidator\n{\n    private readonly User _user;\n    private readonly List<string> _errors = new();\n\n    public UserValidator(User user) => _user = user;\n\n    public UserValidator MustBeAdult()\n    {\n        if (_user.Age < 18)\n            _errors.Add("用户未成年");\n        return this;\n    }\n\n    public UserValidator MustHaveName()\n    {\n        if (string.IsNullOrWhiteSpace(_user.Name))\n            _errors.Add("姓名必填");\n        return this;\n    }\n\n    public void Validate()\n    {\n        if (_errors.Any())\n            throw new ValidationException(string.Join("; ", _errors));\n    }\n}
\n\n

以及一个优雅入口的工厂方法:

\n\n
public static class Validate\n{\n    public static UserValidator User(User user) => new UserValidator(user);\n}
\n\n

步骤3:添加条件结构和上下文

\n\n

真正的C# DSL应该支持分支。实现When:

\n\n
public UserValidator When(bool condition, Func<UserValidator, UserValidator> validatorFunc)\n{\n    if (condition)\n        return validatorFunc(this);\n    return this;\n}
\n\n

使用方式:

\n\n
Validate.User(user)\n    .MustHaveName()\n    .When(user.Age > 18, v => v.MustBeAdult());
\n\n

高级水平:使用表达式树的C# DSL

\n\n

为了构建动态查询(ORM、规则),我们使用Expression<Func<T, bool>>。创建一个规则构建器:

\n\n
public class RuleBuilder<T>\n{\n    private Expression<Func<T, bool>> _expression;\n\n    public RuleBuilder(Expression<Func<T, bool>> expression) => _expression = expression;\n\n    public RuleBuilder<T> And(Expression<Func<T, bool>> other)\n    {\n        var andExpression = Ex                

博客

书籍推荐