一区二区三区在线-一区二区三区亚洲视频-一区二区三区亚洲-一区二区三区午夜-一区二区三区四区在线视频-一区二区三区四区在线免费观看

服務器之家:專注于服務器技術及軟件下載分享
分類導航

PHP教程|ASP.NET教程|Java教程|ASP教程|編程技術|正則表達式|C/C++|IOS|C#|Swift|Android|VB|R語言|JavaScript|易語言|vb.net|

服務器之家 - 編程語言 - ASP.NET教程 - 生成代碼從T到T1、T2、Tn自動生成多個類型的泛型實例代碼

生成代碼從T到T1、T2、Tn自動生成多個類型的泛型實例代碼

2020-06-01 14:50呂毅 ASP.NET教程

這篇文章主要給大家介紹了關于生成代碼從T到T1、T2、Tn自動生成多個類型的泛型的相關資料,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起看看吧

前言

當你想寫一個泛型 <T> 的類型的時候,是否想過兩個泛型參數、三個泛型參數、四個泛型參數或更多泛型參數的版本如何編寫呢?是一個個編寫?類小還好,類大了就杯具!

事實上,在 Visual Studio 中生成代碼的手段很多,本文采用最笨的方式生成,但效果也很明顯——代碼寫得輕松寫得爽!

本文主要給大家介紹了關于從T到T1、T2、Tn自動生成多個類型的泛型的方法,分享出來供大家參考學習,下面話不多說了,來一起看看詳細的介紹吧

我們想要的效果

我們現在有一個泛型的版本:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
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)
 {
  // 做某些事情。
 }
 
 // 做其他事情。
}

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

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
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:

?
1
2
3
4
5
6
7
8
9
<#@ output extension=".txt" #>
<#@ assembly name="System.Xml" #>
<#
 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 本身也是非常強悍的代碼生成方式。

這不是本文的重點,于是感興趣請閱讀官方文檔 Code Generation and T4 Text Templates - Microsoft Docs 學習。

第二種:文件屬性中的自定義工具

右鍵選擇項目中的一個代碼文件,然后選擇“屬性”,你將看到以下內容:

生成代碼從T到T1、T2、Tn自動生成多個類型的泛型實例代碼

就是這里的自定義工具。在這里填寫工具的 Key,那么一旦這個文件保存,就會運行自定義工具生成代碼。

那么 Key 從哪里來?這貨居然是從注冊表拿的!也就是說,如果要在團隊使用,還需要寫一個注冊表項!即便如此,自定義工具本身也是非常強悍的代碼生成方式。

這也不是本文的重點,于是感興趣請閱讀官方文檔 Custom Tools - Microsoft Docs 學習。

第三種:笨笨的編譯生成事件

這算是通常項目用得最多的方式了,因為它可以在不修改用戶開發環境的情況下執行幾乎任何任務。

右鍵項目,選擇屬性,進入“生成事件”標簽:

生成代碼從T到T1、T2、Tn自動生成多個類型的泛型實例代碼

在“預先生成事件命令行”中填入工具的名字和參數,便可以生成代碼。

制作生成泛型代碼的工具

我們新建一個控制臺項目,取名為 CodeGenerator,然后把我寫好的生成代碼粘貼到新的類文件中。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
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()))));
  }
 }
}

這個類中加入了非常多種常見的泛型字符串特征,當然是采用最笨的字符串替換方法。如果感興趣優化優化,可以用正則表達式,或者使用 Roslyn 擴展直接拿語法樹。

于是,在 Program.cs 中調用以上代碼即可完成泛型生成。我寫了一個簡單的版本,可以將每一個命令行參數解析為一個需要進行轉換的泛型類文件。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
using System.IO;
using System.Linq;
using System.Text;
using Walterlv.BuildTools;
 
class Program
{
 static void Main(string[] args)
 {
  foreach (var argument in args)
  {
   GenerateGenericTypes(argument, 4);
  }
 }
 
 private static void GenerateGenericTypes(string file, int count)
 {
  // 讀取原始文件并創建泛型代碼生成器。
  var template = File.ReadAllText(file, Encoding.UTF8);
  var generator = new GenericTypeGenerator(template);
 
  // 根據泛型個數生成目標文件路徑和文件內容。
  var format = GetIndexedFileNameFormat(file);
  (string targetFileName, string targetFileContent)[] contents = Enumerable.Range(2, count - 1).Select(i =>
   (string.Format(format, i), generator.Generate(i))
  ).ToArray();
 
  // 寫入目標文件。
  foreach (var writer in contents)
  {
   File.WriteAllText(writer.targetFileName, writer.targetFileContent);
  }
 }
 
 private static string GetIndexedFileNameFormat(string fileName)
 {
  var directory = Path.GetDirectoryName(fileName);
  var name = Path.GetFileNameWithoutExtension(fileName);
  if (name.EndsWith("1"))
  {
   name = name.Substring(0, name.Length - 1);
  }
 
  return Path.Combine(directory, name + "{0}.cs");
 }
}

考慮到這是 Demo 級別的代碼,我將生成的泛型個數直接寫到了代碼當中。這段代碼的意思是按文件名遞增生成多個泛型類。

例如,有一個泛型類文件 Demo.cs,則會在同目錄生成 Demo2.cs,Demo3.cs,Demo4.cs。當然,Demo.cs 命名為 Demo1.cs 結果也是一樣的。

在要生成代碼的項目中添加“預先生成事件命令行”:

?
1
"$(ProjectDir)..\CodeGenerator\$(OutDir)net47\CodeGenerator.exe" "$(ProjectDir)..\Walterlv.Demo\Generic\IDemoFile.cs" "$(ProjectDir)..\..\Walterlv.Demo\Generic\DemoFile.cs"

現在,編譯此項目,即可生成多個泛型類了。

彩蛋

如果你仔細閱讀了 GenericTypeGenerator 類的代碼,你將注意到我為生成的文件加上了條件編譯符“GENERATED_CODE”。這樣,你便可以使用 #ifdef GENERATED_CODE 來處理部分不需要進行轉換或轉換有差異的代碼了。

這時寫代碼,是不是完全感受不到正在寫模板呢?既有代碼著色,又適用于團隊其他開發者的開發環境。是的,個人認為如果帶來便捷的同時注意不到工具的存在,那么這個工具便是好的。

如果將傳參改為自動尋找代碼文件,將此工具發布到 NuGet,那么可以通過 NuGet 安裝腳本將以上過程全自動化完成。

參考資料

總結

以上就是這篇文章的全部內容了,希望本文的內容對大家的學習或者工作具有一定的參考學習價值,如果有疑問大家可以留言交流,謝謝大家對服務器之家的支持。

原文鏈接:https://walterlv.com/post/generate-code-of-generic-types.html

延伸 · 閱讀

精彩推薦
主站蜘蛛池模板: 富士av105| 国产精品福利在线观看入口 | 高h视频免费观看 | 手机看片自拍自自拍日韩免费 | 日本a在线天堂 | 欧美办公室激情videos高清 | 好吊操这里有精品 | 暖暖视频免费观看视频中国.韩剧 | 欧美破处女视频 | 久久精品热在线观看30 | 亚洲香蕉网久久综合影院3p | 日本一区二区不卡久久入口 | 日韩伦理一区 | 日本福利视频网站 | 色老板成人永久免费视频 | 久久人妻无码毛片A片麻豆 久久热这里只有 精品 | 亚洲视频免费在线观看 | 国产98在线 | 麻豆视频免费在线观看 | 91亚洲精品国产自在现线 | 国产精品免费aⅴ片在线观看 | 精品一区二区三区在线播放 | 秋霞综合网| 成人影院www在线观看 | 欧美日韩国产最新一区二区 | 色卡7707c| 91一个人的在线观看www | 精品视频日本 | 性直播免费| 欧美黑人换爱交换乱理伦片 | 污污免费 | 国产精品久久久久a影院 | 亚洲成色WWW久久网站夜月 | 欧美a级完整在线观看 | 国产九九热视频 | 欧美肥胖老妇做爰变态 | 日韩理论片 | 四虎精品在线视频 | 色播影院性播影院私人影院 | 天堂一区二区在线观看 | 小鸟酱在线播放 |