本文将告诉大家如何在分析器里面解析代码里面对于 ValueTuple 的定义,包括如何获取 ValueTuple 里面的 Item 的类型和命名

开始之前先创建一个用来被分析的项目,在这个项目里面定义 Foo1 类型,然后再定义 F2 方法,设置 F2 方法的返回值是一个 ValueTuple 类型,如以下代码

public class Foo1
{
public (int x, int) F2()
{
return default;
}
}

本文将使用此作为例子,告诉大家如何解析 ValueTuple 的定义,也就是获取 F2 方法返回值类型的定义

先编写语法过滤,只让方法定义的语法通过,如以下代码

    [Generator(LanguageNames.CSharp)]
public class CodeCollectionIncrementalGenerator : IIncrementalGenerator
{
public void Initialize(IncrementalGeneratorInitializationContext context)
{
var incrementalValuesProvider = context.SyntaxProvider.CreateSyntaxProvider((node, _) =>
{
// 只读取方法定义。这里例子是读取一个方法的返回值
return node.IsKind(SyntaxKind.MethodDeclaration);
}, (syntaxContext, _) =>
{
... // 忽略代码
}); ... // 忽略代码
}

因为本文是使用分析 F2 方法的返回值作为例子,因此上面代码只让方法定义的语法通过

接下来获取方法的返回值,如以下代码

            var incrementalValuesProvider = context.SyntaxProvider.CreateSyntaxProvider((node, _) =>
{
// 只读取方法定义。这里例子是读取一个方法的返回值
return node.IsKind(SyntaxKind.MethodDeclaration);
}, (syntaxContext, _) =>
{
// 转换为语义
IMethodSymbol methodSymbol =
syntaxContext.SemanticModel.GetDeclaredSymbol(syntaxContext.Node) as IMethodSymbol;
if (methodSymbol is null)
{
// 理论上不会进入这个代码,前面判断方法定义
return string.Empty;
} // 获取方法返回类型
ITypeSymbol methodSymbolReturnType = methodSymbol.ReturnType;
if (methodSymbolReturnType is INamedTypeSymbol namedTypeSymbol)
{
} ... // 忽略代码 }).Where(t => !string.IsNullOrEmpty(t));

拿到方法返回值,可以进行判断 ValueTuple 类型,判断方法是通过先判断是否值类型,再判断 Tuple 元素的数量

                    // 由于 ValueTuple 是值类型,因此可以快速判断是否值类型
if (namedTypeSymbol.IsValueType && namedTypeSymbol.TupleElements.Length > 0)
{
}

获取 ValueTuple 语义上定义的各个 Item 的类型和命名可以通过遍历 TupleElements 属性

                        foreach (var tupleElement in namedTypeSymbol.TupleElements)
{
// 如此可以获取每一个类型
var tupleElementType = tupleElement.Type;
}

如果用户代码里面没有给 Item 命名,将会自动填充默认的 Item1 等默认命名,如果想要获取原来代码的定义,可以获取语法内容,如以下代码

                        var code = namedTypeSymbol.DeclaringSyntaxReferences[0].GetSyntax().ToString();

以下是 CodeCollectionIncrementalGenerator 的全部代码,可以创建出一个 Foo 类型,用来输出 Foo1 的 F2 方法的返回代码

    [Generator(LanguageNames.CSharp)]
public class CodeCollectionIncrementalGenerator : IIncrementalGenerator
{
public void Initialize(IncrementalGeneratorInitializationContext context)
{
var incrementalValuesProvider = context.SyntaxProvider.CreateSyntaxProvider((node, _) =>
{
// 只读取方法定义。这里例子是读取一个方法的返回值
return node.IsKind(SyntaxKind.MethodDeclaration);
}, (syntaxContext, _) =>
{
// 转换为语义
IMethodSymbol methodSymbol =
syntaxContext.SemanticModel.GetDeclaredSymbol(syntaxContext.Node) as IMethodSymbol;
if (methodSymbol is null)
{
// 理论上不会进入这个代码,前面判断方法定义
return string.Empty;
} // 获取方法返回类型
ITypeSymbol methodSymbolReturnType = methodSymbol.ReturnType;
if (methodSymbolReturnType is INamedTypeSymbol namedTypeSymbol)
{
// 由于 ValueTuple 是值类型,因此可以快速判断是否值类型
if (namedTypeSymbol.IsValueType && namedTypeSymbol.TupleElements.Length > 0)
{
foreach (var tupleElement in namedTypeSymbol.TupleElements)
{
// 如此可以获取每一个类型
var tupleElementType = tupleElement.Type;
} var code = namedTypeSymbol.DeclaringSyntaxReferences[0].GetSyntax().ToString();
return code;
}
} return "";
}).Where(t => !string.IsNullOrEmpty(t)); context.RegisterImplementationSourceOutput(incrementalValuesProvider,
(productionContext, provider) =>
{
var text = provider; var code = @"using System;
namespace LainewihereJerejawwerye
{
public static class Foo
{
public static void F1()
{
Console.WriteLine(""Foo1.F2 方法返回值是" + text + @""");
}
}
}";
productionContext.AddSource("Demo", code);
});
}
}
}

本文的代码放在githubgitee 欢迎访问

可以通过如下方式获取本文的源代码,先创建一个空文件夹,接着使用命令行 cd 命令进入此空文件夹,在命令行里面输入以下代码,即可获取到本文的代码

git init
git remote add origin https://gitee.com/lindexi/lindexi_gd.git
git pull origin ed913bc50dcdbdf67bd387cd49d0cfd2a95b4ede

以上使用的是 gitee 的源,如果 gitee 不能访问,请替换为 github 的源。请在命令行继续输入以下代码

git remote remove origin
git remote add origin https://github.com/lindexi/lindexi_gd.git
git pull origin ed913bc50dcdbdf67bd387cd49d0cfd2a95b4ede

获取代码之后,进入 LainewihereJerejawwerye 文件夹

更多源代码生成,请看官方的 Source Generators Cookbook

更多关于我博客请参阅 博客导航

IIncrementalGenerator 解析 ValueTuple 的定义的更多相关文章

  1. AMF解析之数据类型定义 (转)

    目录(?)[-] OpenRTMFPCumulus Primer15AMF解析之数据类型定义 数据类型 undefined Type null Type false type true type in ...

  2. ES5:深入解析如何js定义类或对象。

    1.原始方式 var oCar = new  Object; oCar.color = "blue"; oCar.showColor = function(){alert(this ...

  3. C#匿名类型和动态解析减少定义传输类模板

    C#作为强类型语言,在序列化和反序列化(json)场景中对字符串解析常常需要定义强类型模板,造成编码上的繁琐.其实可以使用匿名类型和动态解析减少json序列化时候的数据模板定义: string a = ...

  4. XML概念定义以及如何定义xml文件编写约束条件java解析xml DTD XML Schema JAXP java xml解析 dom4j 解析 xpath dom sax

    本文主要涉及:xml概念描述,xml的约束文件,dtd,xsd文件的定义使用,如何在xml中引用xsd文件,如何使用java解析xml,解析xml方式dom sax,dom4j解析xml文件 XML来 ...

  5. Kafka:ZK+Kafka+Spark Streaming集群环境搭建(十四)定义一个avro schema使用comsumer发送avro字符流,producer接受avro字符流并解析

    参考<在Kafka中使用Avro编码消息:Consumer篇>.<在Kafka中使用Avro编码消息:Producter篇> 在了解如何avro发送到kafka,再从kafka ...

  6. Spring3.2 中 Bean 定义之基于 XML 配置方式的源码解析

    Spring3.2 中 Bean 定义之基于 XML 配置方式的源码解析 本文简要介绍了基于 Spring 的 web project 的启动流程,详细分析了 Spring 框架将开发人员基于 XML ...

  7. 使用newtonsoft完美序列化WebApi返回的ValueTuple

    由于开发功能的需要,又懒得新建太多的class,所以ValueTuple是个比较好的偷懒方法,但是,由于WebApi需要返回序列化后的json,默认的序列化只能将ValueTuple定义的各个属性序列 ...

  8. SpringMVC视图解析器

    SpringMVC视图解析器 前言 在前一篇博客中讲了SpringMVC的Controller控制器,在这篇博客中将接着介绍一下SpringMVC视 图解析器.当我们对SpringMVC控制的资源发起 ...

  9. 通俗易懂的 JSon解析处理

    1.主要用到的类: 主要用到了JavaScriptSerializer类,该类在System.Web.Script.Serialization命名空间(在System.Web.Extensions.d ...

  10. PERL/LEX/YACC技术实现文本解析--XML解析

    继周六的p_enum.pl后,再来一篇说说我用perl做的lex,yacc工具.之前说了,我学习lex和yacc的最初动机是为了做个C语言解释器的SHELL:但后来工作中的实际需要也是制作perl版l ...

随机推荐

  1. 记录--怎么写一个可以鼠标控制旋转的div?

    这里给大家分享我在网上总结出来的一些知识,希望对大家有所帮助 说在前面 鼠标控制元素旋转在现在也是一个很常见的功能,让我们从实现div元素的旋转控制开始来了解元素旋转的具体原理和实现方法吧. 效果展示 ...

  2. Kubernetes客户端认证(三)—— Kubernetes使用CertificateSigningRequest方式签发客户端证书

    1.概述 在<Kubernetes客户端认证(一)-- 基于CA证书的双向认证方式>和<Kubernetes客户端认证(二)-- 基于ServiceAccount的JWTToken认 ...

  3. 【Oracle】Oracle表空间数据文件大小设置

    Oracle数据文件默认大小上限是32G,如果要数据文件大于32G,需要在数据库创建之初就设置好. 表空间数据文件容量与DB_BLOCK_SIZE有关,在初始建库时,DB_BLOCK_SIZE要根据实 ...

  4. KingbaseES V8R3集群运维案例之---message length (8) in slot 1 does not match with slot 0(12)故障

    案例说明: 在KingbaseES V8R3集群在连接kingbasecluster服务(9999端口)时,出现"message length (8) in slot 1 does not ...

  5. Makefile 简单学习

    一.Makefile 简介 Makefile 是一种常用于编译的脚本语言.它可以更好更方便的管理你的项目的代码编译,节约编译时间(没改动的文件不编译).注意 Makefile 文件命令必须是 Make ...

  6. Android记账本界面实现

    <!--activity_main.xml-->1 <?xml version="1.0" encoding="utf-8"?> 2 & ...

  7. .NET Core 多线程的用法,以及用例

    1.使用 Thread 类 Thread 类是 .NET 中最基本的多线程操作方式之一,可以使用它创建并启动新线程.以下是一个简单的例子,创建一个新的线程并运行: using System; usin ...

  8. MySQL访问控制和用户管理

    访问控制   你需要给用户提供他们所需的访问权,且仅提供他们所需的访问权.这就是所谓的访问控制,管理访问控制需要创建和管理用户账号. 多数用户只需要对表进行读和写,但少数用户甚至需要能创建和删除表: ...

  9. 做好PPT,提高沟通效率

    本文于2019年7月12日完成,发布在个人博客网站上. 考虑个人博客因某种原因无法修复,于是在博客园安家,之前发布的文章逐步搬迁过来. 只用一套PPT来通杀全部使用场景,这个想法其实很天真. 作为一种 ...

  10. MySQL 主从 AUTO_INCREMENT 不一致问题分析

    作者:vivo 互联网数据库团队 - Wei Haodong 本文介绍了 MySQL5.7 中常见的replace into 操作造成的主从auto_increment不一致现象,一旦触发了主从切换, ...