生成代码自动生成从T到T1、T2和Tn的多种类型的通用实例代码
前言
当你想写一个泛型t类型的时候,你有没有想过如何写两个泛型参数、三个泛型参数、四个泛型参数或者更多泛型参数的版本?是一个个写的?小是可以的,大是好的!
事实上,在Visual Studio中有许多生成代码的方法。本文用最笨的方式生成代码,但效果明显。——代码写起来简单又爽!
主要介绍从T到T1、T2、Tn自动生成多种类型泛型的方法,分享给大家参考。下面就不多说了,我们来看看详细的介绍
我们想要的效果
我们现在有一个通用版本:
公开课DeMop { public Demo(ActionT Demo){ _ Demo=Demo?引发新的ArgumentNullException(name of(action));} private ActionT _ demoPublic async TaskT DoAsync(T t) {//做点什么。}//做其他事情。}您想要生成多个版本的泛型:
T2公开课Demo 1 {公开课Demo(ActionT1,T2 demo) { _demo=demo?引发新的ArgumentNullException(name of(action));}私人行动1,T2 _ demo;Public async task (t1,t2) do async (t1t1,T2 2){//做点什么。}//做其他事情。}请注意,类型的泛型变为多个,参数从一个变为多个,返回值从单个值变为元组。
那么,如何生成呢?
查看Visual Studio生成代码的方式
Visual Studio自带两种代码生成方法。
类型1: T4文本模板
事实上,T4模板是Visual Studio最推荐的方式,因为您只需要编写一个包含占位符的模板文件,Visual Studio就会自动为您填充这些占位符。
那么Visual Studio填充了什么呢?可以,可以在模板文件中写C#代码!例如,官方演示:
#@输出扩展名='。txt' # #@程序集名称='System。Xml' # #系统。Xml文档配置数据=.//在此读取数据文件。#命名空间fabrikam。#=configurationdata。选择单个节点(“作业名”)。值# { 0.//这里有更多代码。}这段代码写在哪里?右键单击项目以创建新项目,然后选择运行时文本模板。
一旦T4模板被编辑和保存(Ctrl-S),代码立即生成。
你觉得这种代码着色很恐怖吗?嗯.完全没有代码着色,OK!即便如此,T4本身也是一种非常强大的代码生成方式。
这不是本文的重点,请阅读官方文档代码生成和T4文本模板-微软文档进行学习。
第二:文件属性中的自定义工具
右键单击项目中的代码文件,然后选择属性,您将看到以下内容:
这是这里的自定义工具。在这里填写工具的Key,文件一旦保存,自定义工具就会运行生成代码。
那么Key从何而来?真不敢相信它是从注册表里拿走的!也就是说,如果你想在一个团队中使用它,你需要写一个注册表项!即便如此,自定义工具本身也是一种非常强大的生成代码的方式。
这不是本文的重点,请阅读官方文档自定义工具-微软文档了解。
第三种类型:愚蠢的编译生成事件
这是项目最常用的方式,因为它几乎可以执行任何任务,而无需修改用户开发环境。
右键单击项目,选择属性,然后进入生成事件选项卡:
在“预示教事件命令行”中填写工具的名称和参数,即可生成代码。
制作生成通用代码的工具
我们创建一个名为CodeGenerator的新控制台项目,然后将我编写的生成代码粘贴到新的类文件中。
使用系统;使用系统Linq .使用静态系统。环境;命名空间沃尔瑟夫.BuildTools{公共类GenericTypeGenerator {私有静态只读字符串生成的标题=[电子邮件保护]'//////自动生成//此代码由工具生成。//运行时版本: {环境版本。ToString(4)}////对此文件的更改可能会导致不正确的行为,并且如果//重新生成代码,这些更改将会丢失///自动生成///-#定义GENERATED _ CODE ';私有静态只读字符串生成的页脚=[电子邮件保护]' ';private readonly string _ genericTemplate;私有只读字符串_ tool name public GenericTypeGenerator(字符串工具名,字符串genericTemplate){ _ tool name=工具名?抛出新的ArgumentNullException(工具名的名称));_ genericTemplate=genericTemplate?抛出新的ArgumentNullException(工具名的名称));}公共字符串生成(int泛型计数){ var tool name=_ tool namevar toolVersion=' 1.0var生成的属性=$ '[系统CodeDom。编译器。生成的代码(\ ' {工具名称} \ ',\ ' {工具版本} \ ')]';var content=_genericTemplate //替换泛型。替换(“out T”,FromTemplate(“{ 0 }”,“out T{n}”,“,”,genericCount)).替换(“TaskT”,FromTemplate(“任务({0})”、“T{n}”、“”、“”、泛型计数)).替换(“FuncT,Task”,FrOmtemplate(“{ FuncT { 0 }”,Task”,“T{n}”,“”,genericCount)).替换(“T,Task”,FromTemplate(“{ 0 },Task”,“T{n}”,“,”,genericCount)).替换('(T,bool ',FromTemplate '({ 0 },bool ',{n} ',',',genericCount)).替换(' var (t ',FromTemplate('var ({0},',' t{n} ',',',genericCount)).替换(',t '),FromTemplate(',{0})',t{n} ',',',genericCount)).替换(' return (t ',FromTemplate('return ({0},',' t{n} ',',',genericCount)).替换(“T”,FrOmtemplate(“{ 0 }”,“T{n}”,“,”,genericCount)).替换('(T值)',从模板(({ 0 })值)',' T{n} ',',',genericCount().替换('(T)',FromTemplate('({0})',' T{n} t{n} ',',',genericCount)).替换('(t)',FromTemplate('({0})',t{n} ',',',genericCount)).替换(' var t=',FromTemplate('var ({0})=',' t{n} ',',',genericCount)).替换(“T”,FrOmtemplate(({ 0 })”,“T{n}”,“,”,genericCount)).替换(' t;FrOmtemplate('({ 0 });' t{n} ',',',genericCount)) //生成[生成代码].替换('公共接口,$ ' {生成的属性} { NewLine }公共接口')。替换(“public class”,$“{ generated attribute } { NewLine } public class”).替换('公共密封类,$ ' {生成的属性} { NewLine }公共密封类');返回GeneratedHeader换行符内容修剪()新行生成的页脚;}私有静态字符串FromTemplate(字符串模板、字符串部分、字符串分隔符、int计数){返回字符串。格式(模板,字符串。联接(分隔符,可枚举。范围(1,计数)。选择(x=零件。替换(“{n}”,x . ToString()))));} }}这个类中加入了非常多种常见的泛型字符串特征,当然是采用最笨的字符串替换方法。如果感兴趣优化优化,可以用正则表达式,或者使用罗斯林扩展直接拿语法树。
于是,在Program.cs中调用以上代码即可完成泛型生成。我写了一个简单的版本,可以将每一个命令行参数解析为一个需要进行转换的泛型类文件。
使用系统。IO;使用系统。Linq使用系统。文字;利用沃尔瑟夫。BuildToolsclass Program { static void Main(string[]args){ foreach(args中的var参数){ GenerateGenericTypes(参数,4);} } private static void generategeneric类型(字符串文件,int count){//读取原始文件并创建一个泛型代码生成器。变量模板=文件。ReadAllText(文件,编码。UTF8);var generator=new GenericTypeGenerator(模板);//根据泛型数量生成目标文件路径和文件内容。var format=GetIndexedFileNameFormat(文件);(字符串目标文件名,字符串目标文件内容)[]内容=可枚举。范围(2,计数- 1)。选择(i=(字符串。格式(格式,I),生成器。生成(I))。ToArray();//写入目标文件。foreach(内容中的变量编写器){ File。writelltext(writer . targetfilename,writer . targetfilecontent);} }私有静态字符串GetIndexedFileNameFormat(字符串文件名){ var directory=Path。GetDirectoryName(文件名);变量名称=路径。getfilename不带text extension(文件名);if(名称。EndsWith(' 1 '){ name=name。子字符串(0,名称。长度-1);}返回路径。组合(目录,名称“{0}”。cs’);}}考虑到这是Demo级别的代码,我将生成的泛型数量直接写入了代码中。这段代码意味着按照文件名的升序生成多个泛型类。
例如,如果有一个通用类文件Demo.cs,Demo2.cs、Demo3.cs和Demo4.cs将在同一个目录下生成。当然,把Demo.cs命名为Demo1.cs的结果是一样的。
将“预先生成的事件命令行”添加到项目中以生成代码:
$ (projectdir).\ code generator \ $(outdir)net47 \ code generator . exe ' ' $(project dir).\ Walter LV . demo \ generic \ idemofile . cs ' ' $(project dir).\.\ walterlv.demo \。
复活节彩蛋
如果您仔细阅读GenericTypeGenerator类的代码,您会注意到我在生成的文件中添加了条件编译器“GENERATED_CODE”。这样,您就可以使用#ifdef GENERATED_CODE来处理一些不需要转换或者在转换上有差异的代码。
当你写代码的时候,你觉得你在写模板吗?它不仅是代码着色,也适合团队中其他开发人员的开发环境。是的,个人认为,如果你在带来便利的同时没有注意到工具的存在,那么这个工具就是好的。
如果您将参数传递更改为自动查找代码文件并将此工具发布到NuGet,则上述过程可以通过NuGet安装脚本完全自动化。
参考材料
代码生成和T4文本模板-微软文档定制工具-微软文档摘要
以上就是本文的全部内容。希望本文的内容对大家的学习或工作有一定的参考价值。有问题可以留言交流。谢谢你的支持。
版权声明:生成代码自动生成从T到T1、T2和Tn的多种类型的通用实例代码是由宝哥软件园云端程序自动收集整理而来。如果本文侵犯了你的权益,请联系本站底部QQ或者邮箱删除。