前言

最近在捣鼓代码生成器,基于 Roslyn,我们可以让生成器项目生成我们的目标 C# 代码,这个也是MVVM Toolkit的实现方式,在查看生成代码的过程中,我们经常会遇到一些特殊的特性,如 GeneratedCodeAttribute ,刚好我还遇到过 CompilerGeneratedAttribute。感觉两个特性差不多,都可以用于标识代码的生成来源,帮助开发者和其他工具更好地理解和处理代码。

GeneratedCodeAttribute 解析

定义与用途

GeneratedCodeAttribute 是一个系统提供的特性,定义在 System.CodeDom.Compiler 命名空间,用于标记由工具或编译器生成的代码。它通常包含两个参数:生成工具的名称和版本号。

[AttributeUsage(AttributeTargets.All, Inherited = false)]
public sealed class GeneratedCodeAttribute : Attribute
{
public string Tool { get; }
public string Version { get; } public GeneratedCodeAttribute(string tool, string version)
{
Tool = tool;
Version = version;
}
}

这个特性的主要用途是:

  • 标识生成的代码:当你使用 Source Generator、T4 模板、Roslyn API 或其他代码生成工具时,可以在生成的文件中添加这个特性,以明确指出代码是由哪个工具生成的。
  • 避免误修改:标记为 GeneratedCode 的代码可以提醒开发者不要直接编辑这些文件,因为它们是自动生成的,任何手动修改可能会在下次生成时丢失。
  • 分析器和工具支持:某些分析器(如 Roslyn 分析器)和工具会识别并特殊处理带有 GeneratedCodeAttribute 的代码,例如忽略代码覆盖率统计或特定的代码分析规则。

示例

假设你有一个 Source Generator 工具名为 MyCustomTool,版本为 1.0.0,你可以这样标记生成的代码:

[GeneratedCode("MyCustomTool", "1.0.0")]
public partial class MyClass
{
// 自动生成的代码
}

CompilerGeneratedAttribute 解析

定义与用途

CompilerGeneratedAttribute 定义在 System.Runtime.CompilerServices 命名空间,是一个更具体的特性,用于标记由 C# 编译器自动生成的代码片段。它没有参数,仅表示代码是由编译器生成的。

[AttributeUsage(AttributeTargets.All, Inherited = false)]
public sealed class CompilerGeneratedAttribute : Attribute
{
}

这个特性的主要用途是:

  • 标识编译器生成的代码:当编译器为了实现某些语言特性(如匿名类型、迭代器、异步方法等)而自动生成代码时,会自动添加这个特性。这有助于区分用户编写的代码和编译器生成的代码。
  • 内部实现细节:这个特性主要用于内部实现细节,普通开发者通常不需要手动添加它。它是编译器用来标记其生成的代码的一种方式。

示例

编译器生成的代码片段可能如下所示:

[CompilerGenerated]
private sealed class <>c__DisplayClass1_0
{
public int x; internal void <Method>b__0()
{
Console.WriteLine(x);
}
}

区别与选择

虽然 GeneratedCodeAttributeCompilerGeneratedAttribute 都用于标识代码的生成来源,但它们有着不同的用途和适用场景。

  • 来源不同

    • GeneratedCodeAttribute 通常由外部工具或源代码生成器添加,以标识代码是由某个工具生成的,一般来说是出于编码人员的自身目标。
    • CompilerGeneratedAttribute 由 C# 编译器自身添加,用于标识编译器生成的代码片段。
  • 应用场景

    • 如果你正在开发 Source Generator 或其他代码生成工具,并希望标记生成的代码以便后续处理或提醒开发者不要直接编辑这些文件,应该手动标记使用 GeneratedCodeAttribute
    • 不应该手动添加该特性,如果你在查看编译后的代码,发现带有 CompilerGeneratedAttribute 的类或成员,这通常是编译器为了实现某些语言特性而生成的代码,不应被手动修改。

代码生成器应用示例

MVVM Toolkit 就按照这个标准开发,假设你正在开发一个 Source Generator 来生成部分类文件:

using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using System.CodeDom.Compiler; public class MySourceGenerator : ISourceGenerator
{
public void Initialize(GeneratorInitializationContext context)
{
// Initialization logic if needed
} public void Execute(GeneratorExecutionContext context)
{
var sourceBuilder = new StringBuilder();
sourceBuilder.AppendLine("[GeneratedCode(\"MyCustomTool\", \"1.0.0\")]");
sourceBuilder.AppendLine("public partial class MyClass");
sourceBuilder.AppendLine("{");
sourceBuilder.AppendLine(" public string MyProperty { get; set; }");
sourceBuilder.AppendLine("}"); context.AddSource("MyClass.g.cs", sourceBuilder.ToString());
}
}

上面代码中,GeneratedCodeAttribute 被用来标记生成的代码,确保其他工具和开发者知道这段代码是由 MyCustomTool 生成的。

一些建议:

  • 不适用于用户可修改的模板:如果有一个代码生成工具生成的模板,用户可能会根据需要对其进行修改,那么就不应该使用 GeneratedCodeAttribute 标记这些模板。因为一旦代码被手动修改,再用 GeneratedCodeAttribute 标记就不再准确了,而且可能会误导其他工具忽略这些手动修改的内容。
  • 部分类的特殊处理:当生成的代码是部分类的一部分时,不要在整个类上应用 GeneratedCodeAttribute。相反,你应该仅将此特性应用于该部分类中生成的具体成员(如方法、字段、属性等)。这是因为部分类可以有多个文件定义,而用户可能在其他文件中添加自己的实现。通过只标记生成的成员,你可以确保只有自动生成的部分被正确标识,而不会影响用户添加的代码。(这个可以看 MVVM Toolkit 生成的代码)

总结

简单说来:

  • GeneratedCodeAttribute 主要用于标记由工具或编译器生成的代码,特别是那些会频繁重新生成的代码。这有助于开发者和其他工具识别这些代码片段,并避免对它们进行不必要的修改。
  • CompilerGeneratedAttribute 一般不要手动添加到代码中。

参考文献

CompilerGenerated与GeneratedCode区别的更多相关文章

  1. Java中serialVersionUID的解释及两种生成方式的区别(转载)

    转载自:http://blog.csdn.net/xuanxiaochuan/article/details/25052057 serialVersionUID作用:        序列化时为了保持版 ...

  2. 从yield关键字看IEnumerable和Collection的区别

    C#的yield关键字由来以久,如果我没有记错的话,应该是在C# 2.0中被引入的.相信大家此关键字的用法已经了然于胸,很多人也了解yield背后的“延迟赋值”机制.但是即使你知道这个机制,你也很容易 ...

  3. c#与java的区别

    经常有人问这种问题,用了些时间java之后,发现这俩玩意除了一小部分壳子长的还有能稍微凑合上,基本上没什么相似之处,可以说也就是马甲层面上的相似吧,还是比较短的马甲... 一般C#多用于业务系统的开发 ...

  4. jquery和Js的区别和基础操作

    jqery的语法和js的语法一样,算是把js升级了一下,这两种语法可以一起使用,只不过是用jqery更加方便 一个页面想要使用jqery的话,先要引入一下jqery包,jqery包从网上下一个就可以, ...

  5. 【原】nodejs全局安装和本地安装的区别

    来微信支付有2年多了,从2年前的互联网模式转变为O2O模式,主要的场景是跟线下的商户去打交道,不像以往的互联网模式,有产品经理提需求,我们帮忙去解决问题. 转型后是这样的,团队成员更多需要去寻找业务的 ...

  6. 探究@property申明对象属性时copy与strong的区别

    一.问题来源 一直没有搞清楚NSString.NSArray.NSDictionary--属性描述关键字copy和strong的区别,看别人的项目中属性定义有的用copy,有的用strong.自己在开 ...

  7. X86和X86_64和X64有什么区别?

    x86是指intel的开发的一种32位指令集,从386开始时代开始的,一直沿用至今,是一种cisc指令集,所有intel早期的cpu,amd早期的cpu都支持这种指令集,ntel官方文档里面称为&qu ...

  8. Java中Comparable与Comparator的区别

    相同 Comparable和Comparator都是用来实现对象的比较.排序 要想对象比较.排序,都需要实现Comparable或Comparator接口 Comparable和Comparator都 ...

  9. MySQL中interactive_timeout和wait_timeout的区别

    在用mysql客户端对数据库进行操作时,打开终端窗口,如果一段时间没有操作,再次操作时,常常会报如下错误: ERROR (HY000): Lost connection to MySQL server ...

  10. 设置line-height:1.5和line-height:150%或者line-height:150px的区别

    直接正题: 看一下line-height可能的值: 其实可以分为两类: (1)不带单位的(如line-height:1.5),这种是推荐使用的: (2)带单位的(如line-heigth:30px/1 ...

随机推荐

  1. query和params的区别

    2者都是编程式路由跳转用来存放传递数据的位置,query使用path引入,数据显示在地址栏上,params使用name引入,传递的数据在页面看不见 :

  2. 14 Positional Encoding (为什么 Self-Attention 需要位置编码)

    博客配套视频链接: https://space.bilibili.com/383551518?spm_id_from=333.1007.0.0 b 站直接看 配套 github 链接:https:// ...

  3. log4j2配置自定义filter报错Unable to invoke factory method in class

    报错Unable to invoke factory method in class com....log4j.ScheduleLoggerFilter 2020-05-13 16:32:35,613 ...

  4. SqlUtils 使用

    一.前言 随着 Solon 3.0 版本发布,新添加的 SqlUtils 接口,用于操作数据库,SqlUtils 是对 Jdbc 原始接口的封装.适合 SQL 极少或较复杂,或者 ORM 不适合的场景 ...

  5. DiTAC:不知如何提升性能?试试这款基于微分同胚变换的激活函数 | ECCV'24

    非线性激活函数对深度神经网络的成功至关重要,选择合适的激活函数可以显著影响其性能.大多数网络使用固定的激活函数(例如,ReLU.GELU等),这种选择可能限制了它们的表达能力.此外,不同的层可能从不同 ...

  6. 使用Radzen Blazor组件库开发的基于ABP框架炫酷UI主题

    一.项目简介 使用过ABP框架的童鞋应该知道它也自带了一款免费的Blazor UI主题,它的页面是长这样的: 个人感觉不太美观,于是网上搜了很多Blazor开源组件库,发现有一款样式非常不错的组件库, ...

  7. [图像处理] 基于CleanVision库清洗图像数据集

    CleanVision是一个开源的Python库,旨在帮助用户自动检测图像数据集中可能影响机器学习项目的常见问题.该库被设计为计算机视觉项目的初步工具,以便在应用机器学习之前发现并解决数据集中的问题. ...

  8. 噢!JavaScript (2):对数组要小心使用delete

    最近在重写我自己的静态博客生成器,虽然遇到的小问题挺多,但今早这个问题令我印象深刻. 发现问题 博客的文章基础数据储存在main.json中,其中专门有数组dateindex来储存经过排列后的文章顺序 ...

  9. TypeError: __init__() got an unexpected keyword argument 'XXXXXXXX'

    解决方法:安装包的版本兼容问题,更新一下安装包的版本(如果在别的机器不报错,那就把不报错的这台机器上的安装包pip freeze > requirements.txt 下来,在报错的机器上安装一 ...

  10. awk优化钉钉通知测试报告

    一.背景 在之前的博客 Go服务自动触发单元测试覆盖率 中钉钉通知的效果实现如下图: 最近RD提出对本次和上次覆盖率的比对需求,并把比对结果也显示在钉钉通知上. 二.实现思路 要实现数据比对,就需要对 ...