前言

最近在捣鼓代码生成器,基于 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. el-table 宽度自适应bug

    和 flex 一起使用的时候会有这个问题.只能自动变宽不能自动变窄. 在  flex-grow:1;  的那一层设置  overflow: auto;

  2. USB2.0 的LPM和USB3.0的LPM区别

    USB 2.0 和 USB 3.0 都支持低功耗管理机制(LPM,Link Power Management),但两者的实现方式和目标不同.以下是 USB 2.0 的 LPM 和 USB 3.0 的 ...

  3. Java日期时间API系列31-----Jdk8中java.time包中的新的日期时间API类,时间戳的获取方式对比、转换和使用。

    时间戳是指格林威治时间1970年01月01日00时00分00秒起至现在的总毫秒数,是所有时间的基础,其他时间可以通过时间戳转换得到.Java中本来已经有相关获取时间戳的方法,Java8后增加新的类In ...

  4. nodejs 和 npm 版本对应关系

    一.nodejs 和 npm 的版本是有适配的 首先看下官网列明的大概匹配关系: 官网链接地址:https://nodejs.org/zh-cn/about/previous-releases 可以查 ...

  5. Windows Server 2012 R2安装JDK7报错:内部错误61003

    按顺序安装 1)KB2919442补丁(https://www.microsoft.com/zh-cn/download/details.aspx?id=42153) 2)KB2919355补丁(ht ...

  6. CSP模拟 小 trick 总结 (持续施工中)

    虽然这篇博客来的有点晚,但还是写了,欢迎dalao补充( (很杂,建议先浏览目录) 1.分块.莫队有关: \(\color{brown}(1)一个真正的回滚莫队(感谢\ Qyun\ 的讲解):\) $ ...

  7. Module Warning (from ./node_modules/postcss-loader/dist/cjs.js): Warning

    .markdown-body { line-height: 1.75; font-weight: 400; font-size: 16px; overflow-x: hidden; color: rg ...

  8. Java新特性--方法引用

    常见方法引用 方法引用可以让你重复使用现有的方法定义,并像Lambda一样传递它们. 方法引用可以看做是仅仅调用特定方法的Lambda表达式的一种便捷写法.类似于Lambda表达式,方法引用不能独立存 ...

  9. 人口分析实战(利用jupyter)

    目录 1.项目需求 2.开始操作 2.1导入我们所需要的包 2.2导入数据.查看原始数据 2.3对数据进行清洗 2.4对数据进行处理 1.项目需求 需求: 导入文件,查看原始数据 将人口数据和各州简称 ...

  10. python中os模块的方法总结

    #返回当前的工作目录os.getcwd #print(os.getcwd()) #改变一个目录 chdir(path) #列出所有的文件或者目录 listdir(path) #print(os.lis ...