聊一聊 C#中有趣的 SourceGenerator生成器

简介: 【10月更文挑战第1天】SourceGenerator 是 C# 中的一项强大功能,允许在编译时动态生成源代码,减少手动编写重复代码的工作量,并可在编译时进行优化和错误检查。它与 Roslyn 编译器紧密集成,可根据预定义逻辑生成新代码。应用场景包括自动生成接口实现和优化计算逻辑等。虽然能显著提升生产力和代码一致性,但开发和调试 SourceGenerator 本身较为复杂,维护成本较高。
  1. SourceGenerator 简介
  • SourceGenerator 是 C# 中的一个强大功能,它允许在编译时生成 C# 源代码。本质上,它是一种编译器 API 扩展,能够根据用户定义的规则在编译阶段动态地生成代码。这样可以减少手动编写重复性代码的工作量,并且可以在编译时进行优化和错误检查。
  • 例如,在处理数据访问层时,如果需要为每个数据库表创建对应的实体类和数据访问方法,使用 SourceGenerator 可以根据数据库表结构的元数据自动生成这些类和方法,而不是手动编写大量相似的代码。
  1. 工作原理
  • 编译时执行:SourceGenerator 在编译过程中运行。当编译器开始处理项目时,它会查找并加载所有的 SourceGenerator。这些生成器会分析项目中的编译单元(比如类、接口等),并根据预定义的逻辑生成新的源代码。
  • 与编译管道集成:它与 Roslyn 编译器紧密集成。Roslyn 编译器会将解析后的语法树等编译信息提供给 SourceGenerator。生成器可以利用这些信息来检查现有代码的结构,例如查找特定的属性、方法或类型声明。然后,基于这些检查结果,生成器可以创建新的语法树,这些语法树最终会被编译成新的 C# 代码并添加到编译输出中。
  1. 实际应用场景
  • 自动实现接口
  • 假设我们有一个接口IRepository<T>,它定义了基本的数据库操作方法,如GetAllGetByIdAddUpdateDelete。我们可以创建一个 SourceGenerator,当一个类声明实现了这个接口时,自动生成接口方法的默认实现。
  • 例如,对于一个CustomerRepository类实现IRepository<Customer>,生成器可以生成类似以下的代码来实现接口方法:


public class CustomerRepository : IRepository<Customer>
{
    public IEnumerable<Customer> GetAll()
    {
        // 这里可以是实际从数据库获取数据的逻辑,暂时返回空集合
        return Enumerable.Empty<Customer>();
    }
    public Customer GetById(int id)
    {
        // 假设的获取单个对象的逻辑,暂时返回null
        return null;
    }
    public void Add(Customer entity)
    {
        // 假设的添加对象到数据库的逻辑
    }
    public void Update(Customer entity)
    {
        // 假设的更新对象在数据库中的逻辑
    }
    public void Delete(Customer entity)
    {
        // 假设的从数据库删除对象的逻辑
    }
}


  • 代码优化和缓存
  • 在处理一些复杂的计算逻辑时,比如计算斐波那契数列。我们可以创建一个 SourceGenerator 来生成缓存逻辑。当一个方法频繁调用计算斐波那契数列时,生成器可以生成代码来缓存已经计算过的值,以提高性能。
  • 例如,原始的斐波那契数列计算方法可能是这样:


public class FibonacciCalculator
{
    public int Calculate(int n)
    {
        if (n <= 1)
        {
            return n;
        }
        return Calculate(n - 1) + Calculate(n - 2);
    }
}


  • 生成器可以生成带有缓存的代码:


public class FibonacciCalculator
{
    private static Dictionary<int, int> _cache = new Dictionary<int, int>();
    public int Calculate(int n)
    {
        if (_cache.ContainsKey(n))
        {
            return _cache[n];
        }
        if (n <= 1)
        {
            return n;
        }
        int result = Calculate(n - 1) + Calculate(n - 2);
        _cache[n] = result;
        return result;
    }
}


  1. 开发 SourceGenerator 的步骤
  • 创建生成器项目:首先,需要创建一个类库项目来包含 SourceGenerator 代码。这个项目应该引用Microsoft.CodeAnalysis.CSharp等相关的编译器 API 库。
  • 实现生成器类:创建一个类,该类实现ISourceGenerator接口。这个接口有两个主要方法:InitializeExecuteInitialize方法用于初始化生成器,例如注册语法接收器(SyntaxReceiver)等。Execute方法是实际生成代码的地方,它会接收一个GeneratorExecutionContext对象,通过这个对象可以获取编译单元的语法树,以及向编译输出添加生成的代码。
  • 语法分析和代码生成:在Execute方法中,可以使用语法分析器来分析现有的代码语法树。例如,查找特定的属性或类型声明。然后,根据分析结果,使用SyntaxFactory等工具来创建新的语法树,代表要生成的代码。最后,通过GeneratorExecutionContext将生成的代码添加到编译输出中。
  1. 优点和局限性
  • 优点
  • 提高生产力:减少了手动编写重复代码的工作量,特别是在处理大量相似代码结构的场景下,如实体类和数据访问层的创建。
  • 性能优化:可以在编译时进行优化,如生成缓存代码,从而提高应用程序的运行时性能。
  • 代码一致性:生成的代码遵循预定义的规则,保证了代码结构和风格的一致性。
  • 局限性
  • 复杂性:开发 SourceGenerator 本身可能比较复杂,需要对 C# 编译器 API、语法树等知识有深入的了解。
  • 调试困难:由于是在编译时运行,调试生成器可能比调试普通的运行时代码更具挑战性。
  • 维护成本:如果生成器的逻辑变得复杂,维护和更新生成器代码可能会带来一定的成本。


相关文章
|
C#
使用C#实现随机数生成器
在许多编程任务中,我们经常需要生成随机数。C#编程语言提供了用于生成伪随机数的内置类库。本篇博客将介绍如何使用C#来实现一个简单的随机数生成器。
309 0
|
算法 C#
开源Math.NET基础数学类库使用(13)C#实现其他随机数生成器
原文:【原创】开源Math.NET基础数学类库使用(13)C#实现其他随机数生成器                本博客所有文章分类的总目录:http://www.cnblogs.com/asxinyu/p/4288836.html 开源Math.NET基础数学类库使用总目录:http://www.cnblogs.com/asxinyu/p/4329737.html 前言   真正意义上的随机数(或者随机事件)在某次产生过程中是按照实验过程中表现的分布概率随机产生的,其结果是不可预测的,是不可见的。
751 0
|
C# 自然语言处理 C++
C#版二维码生成器附皮肤下载
原文 C#版二维码生成器附皮肤下载 前言   本文所使用的二维码生成代码是谷歌开源的条形码图像处理库完成的,c#版的代码可去https://code.google.com/p/zxing/downloads/list下载压缩包。
1142 0
|
C#
c# 福彩双色球号码生成器
图片:完整代码下载:点此下载 一:通过操作GDI+的不合理的解决方案数字在不断变换的时候重绘    基本上都会让程序当掉或许是我的程序没写好,希望达人指教 Codeusing System;using System.
1178 0
|
2月前
|
C# 开发者
C# 一分钟浅谈:Code Contracts 与契约编程
【10月更文挑战第26天】本文介绍了 C# 中的 Code Contracts,这是一个强大的工具,用于通过契约编程增强代码的健壮性和可维护性。文章从基本概念入手,详细讲解了前置条件、后置条件和对象不变量的使用方法,并通过具体代码示例进行了说明。同时,文章还探讨了常见的问题和易错点,如忘记启用静态检查、过度依赖契约和性能影响,并提供了相应的解决建议。希望读者能通过本文更好地理解和应用 Code Contracts。
41 3
|
12天前
|
存储 安全 编译器
学懂C#编程:属性(Property)的概念定义及使用详解
通过深入理解和使用C#的属性,可以编写更清晰、简洁和高效的代码,为开发高质量的应用程序奠定基础。
57 12
|
2月前
|
设计模式 C# 图形学
Unity 游戏引擎 C# 编程:一分钟浅谈
本文介绍了在 Unity 游戏开发中使用 C# 的基础知识和常见问题。从 `MonoBehavior` 类的基础用法,到变量和属性的管理,再到空引用异常、资源管理和性能优化等常见问题的解决方法。文章还探讨了单例模式、事件系统和数据持久化等高级话题,旨在帮助开发者避免常见错误,提升游戏开发效率。
68 4