生成代码从T到T1、T2、Tn自动生成多个类型的泛型

当你想写一个泛型 <T> 的类型的时候,是否想过两个泛型参数、三个泛型参数、四个泛型参数或更多泛型参数的版本如何编写呢?是一个个编写?类小还好,类大了就杯具!

事实上,在 Visual Studio 中生成代码的手段很多,本文采用最笨的方式生成,但效果也很明显——代码写得轻松写得爽!

本文主要给大家介绍了关于从T到T1、T2、Tn自动生成多个类型的泛型的方法,分享出来供大家参考学习,下面话不多说了,来一起看看详细的介绍吧

我们想要的效果

我们现在有一个泛型的版本:

public class Demo<T> { public Demo(Action<T> demo) { _demo = demo ?? throw new ArgumentNullException(nameof(action)); } private Action<T> _demo; public async Task<T> DoAsync(T t) { // 做某些事情。 } // 做其他事情。 }

希望生成多个泛型的版本:

public class Demo<T1, T2> { public Demo(Action<T1, T2> demo) { _demo = demo ?? throw new ArgumentNullException(nameof(action)); } private Action<T1, T2> _demo; public async Task<(T1, T2)> DoAsync(T1 t1, T2 t2) { // 做某些事情。 } // 做其他事情。 }

注意到类型的泛型变成了多个,参数从一个变成了多个,返回值从单个值变成了元组。

于是,怎么生成呢?

回顾 Visual Studio 那些生成代码的方式

Visual Studio 原生自带两种代码生成方式。

第一种:T4 文本模板

事实上 T4 模板算是 Visual Studio 最推荐的方式了,因为你只需要编写一个包含占位符的模板文件,Visual Studio 就会自动为你填充那些占位符。

那么 Visual Studio 用什么填充?是的,可以在模板文件中写 C# 代码!比如官方 DEMO:

<#@ output extension=".txt" #> <#@ assembly #> <# System.Xml.XmlDocument configurationData = ...; // Read a data file here. #> namespace Fabrikam.<#= configurationData.SelectSingleNode("jobName").Value #> { ... // More code here. }

这代码写哪儿呢?在项目上右键新建项,然后选择“运行时文本模板”。

生成代码从T到T1、T2、Tn自动生成多个类型的泛型

T4 模板编辑后一旦保存(Ctrl+S),代码立刻生成。

有没有觉得这代码着色很恐怖?呃……根本就没有代码着色好吗!即便如此,T4 本身也是非常强悍的代码生成方式。

这不是本文的重点,于是感兴趣请阅读官方文档 学习。

第二种:文件属性中的自定义工具

右键选择项目中的一个代码文件,然后选择“属性”,你将看到以下内容:

生成代码从T到T1、T2、Tn自动生成多个类型的泛型

就是这里的自定义工具。在这里填写工具的 Key,那么一旦这个文件保存,就会运行自定义工具生成代码。

那么 Key 从哪里来?这货居然是从注册表拿的!也就是说,如果要在团队使用,还需要写一个注册表项!即便如此,自定义工具本身也是非常强悍的代码生成方式。

这也不是本文的重点,于是感兴趣请阅读官方文档 学习。

第三种:笨笨的编译生成事件

这算是通常项目用得最多的方式了,因为它可以在不修改用户开发环境的情况下执行几乎任何任务。

右键项目,选择属性,进入“生成事件”标签:

生成代码从T到T1、T2、Tn自动生成多个类型的泛型

在“预先生成事件命令行”中填入工具的名字和参数,便可以生成代码。

制作生成泛型代码的工具

我们新建一个控制台项目,取名为 CodeGenerator,然后把我写好的生成代码粘贴到新的类文件中。

using System; using System.Linq; using static System.Environment; namespace Walterlv.BuildTools { public class GenericTypeGenerator { private static readonly string GeneratedHeader = $@"//------------------------------------------------------------------------------ // <auto-generated> // 此代码由工具生成。 // 运行时版本:{Environment.Version.ToString(4)} // // 对此文件的更改可能会导致不正确的行为,并且如果 // 重新生成代码,这些更改将会丢失。 // </auto-generated> //------------------------------------------------------------------------------ #define GENERATED_CODE "; private static readonly string GeneratedFooter = $@""; private readonly string _genericTemplate; private readonly string _toolName; public GenericTypeGenerator(string toolName, string genericTemplate) { _toolName = toolName ?? throw new ArgumentNullException(nameof(toolName)); _genericTemplate = genericTemplate ?? throw new ArgumentNullException(nameof(toolName)); } public string Generate(int genericCount) { var toolName = _toolName; var toolVersion = "1.0"; var GeneratedAttribute = $"[System.CodeDom.Compiler.GeneratedCode(\"{toolName}\", \"{toolVersion}\")]"; var content = _genericTemplate // 替换泛型。 .Replace("<out T>", FromTemplate("<{0}>", "out T{n}", ", ", genericCount)) .Replace("Task<T>", FromTemplate("Task<({0})>", "T{n}", ", ", genericCount)) .Replace("Func<T, Task>", FromTemplate("Func<{0}, Task>", "T{n}", ", ", genericCount)) .Replace(" T, Task>", FromTemplate(" {0}, Task>", "T{n}", ", ", genericCount)) .Replace("(T, bool", FromTemplate("({0}, bool", "T{n}", ", ", genericCount)) .Replace("var (t, ", FromTemplate("var ({0}, ", "t{n}", ", ", genericCount)) .Replace(", t)", FromTemplate(", {0})", "t{n}", ", ", genericCount)) .Replace("return (t, ", FromTemplate("return ({0}, ", "t{n}", ", ", genericCount)) .Replace("<T>", FromTemplate("<{0}>", "T{n}", ", ", genericCount)) .Replace("(T value)", FromTemplate("(({0}) value)", "T{n}", ", ", genericCount)) .Replace("(T t)", FromTemplate("({0})", "T{n} t{n}", ", ", genericCount)) .Replace("(t)", FromTemplate("({0})", "t{n}", ", ", genericCount)) .Replace("var t =", FromTemplate("var ({0}) =", "t{n}", ", ", genericCount)) .Replace(" T ", FromTemplate(" ({0}) ", "T{n}", ", ", genericCount)) .Replace(" t;", FromTemplate(" ({0});", "t{n}", ", ", genericCount)) // 生成 [GeneratedCode]。 .Replace(" public interface ", $" {GeneratedAttribute}{NewLine} public interface ") .Replace(" public class ", $" {GeneratedAttribute}{NewLine} public class ") .Replace(" public sealed class ", $" {GeneratedAttribute}{NewLine} public sealed class "); return GeneratedHeader + NewLine + content.Trim() + NewLine + GeneratedFooter; } private static string FromTemplate(string template, string part, string separator, int count) { return string.Format(template, string.Join(separator, Enumerable.Range(1, count).Select(x => part.Replace("{n}", x.ToString())))); } } }

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/wdsfzs.html