T 부터 T1,T2,Tn 까지 다양한 종류의 인 스 턴 스 코드 를 자동 으로 생 성 합 니 다.

머리말
범 형의 종 류 를 쓰 고 싶 을 때,두 개의 범 형 파라미터,세 개의 범 형 파라미터,네 개의 범 형 파라미터 또는 더 많은 범 형 파라미터 의 버 전 을 어떻게 작성 할 지 생각해 보 셨 습 니까?하나씩 쓰 는 거 야?종류 가 작 으 면 괜찮아,종류 가 크 면 잔 도구!
사실 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\#코드 를 쓸 수 있 습 니 다!예 를 들 어 공식 데모:

<#@ 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. 
} 
이 코드 는 어디 에 쓰 입 니까?항목 에서 새 항목 을 오른쪽 클릭 하고'실행 시 텍스트 템 플 릿'을 선택 하 십시오.

T4 템 플 릿 을 편집 한 후 저장(Ctrl+S)하면 코드 가 즉시 생 성 됩 니 다.
이 코드 착색 무 섭 지 않 아 요?어...코드 착색 이 전혀 없어 요!그럼 에 도 T4 자체 가 강력 한 코드 생 성 방식 이다.
이것 은 본문의 중점 이 아니 므 로 관심 이 있 으 면 공식 문 서 를 읽 으 세 요Code Generation and T4 Text Templates - Microsoft Docs공부 하 세 요.
두 번 째:파일 속성 에 있 는 사용자 정의 도구
오른쪽 단 추 를 누 르 면 항목 의 코드 파일 을 선택 하고'속성'을 선택 하면 다음 과 같은 내용 을 볼 수 있 습 니 다.

바로 이곳 의 사용자 정의 도구 입 니 다.여기에 도구 의 키 를 입력 하면 이 파일 이 저장 되면 사용자 정의 도구 생 성 코드 가 실 행 됩 니 다.
그럼 키 는 어디서 왔어요?이 물건 은 뜻밖에도 등록 표 에서 가 져 온 것 이다!즉,팀 에서 사용 하려 면 레 지 스 트 항목 을 하나 더 써 야 한 다 는 것 이다!그럼 에 도 불구 하고 사용자 정의 도구 자체 가 매우 강 한 코드 생 성 방식 이다.
이것 도 본문의 중점 이 아니 므 로 관심 이 있 으 면 공식 문 서 를 읽 으 세 요Custom Tools - Microsoft Docs공부 하 세 요.
세 번 째:멍청 한 컴 파일 생 성 이벤트
이것 은 사용자 개발 환경 을 수정 하지 않 고 거의 모든 임 무 를 수행 할 수 있 기 때문에 일반적인 프로젝트 가 가장 많이 사용 하 는 방식 이 라 고 할 수 있다.
항목 을 오른쪽 단추 로 눌 러 속성 을 선택 하고"이벤트 생 성"탭 에 들 어 갑 니 다:

"이벤트 명령 줄 미리 생 성"에 도구 의 이름과 파 라 메 터 를 입력 하면 코드 를 생 성 할 수 있 습 니 다.
범용 코드 생 성 도구 만 들 기
우 리 는 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()))));
  }
 }
}
이 클래스 에는 흔히 볼 수 있 는 일반적인 문자열 특징 이 많이 들 어 있 습 니 다.물론 가장 멍청 한 문자열 교체 방법 을 사용 합 니 다.최적화 에 관심 이 있다 면 정규 표현 식 을 사용 하거나 Roslyn 확장 을 사용 하여 문법 트 리 를 직접 가 져 갈 수 있 습 니 다.
따라서 Program.cs 에서 상기 코드 를 호출 하면 일반적인 생 성 을 완성 할 수 있 습 니 다.나 는 모든 명령 행 인 자 를 변환 해 야 할 일반적인 파일 로 해석 할 수 있 는 간단 한 버 전 을 썼 다.

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.cs 가 있 으 면 같은 디 렉 터 리 에서 Demo 2.cs,Demo 3.cs,Demo 4.cs 를 생 성 합 니 다.물론 Demo.cs 를 Demo 1.cs 라 고 명명 한 결과 도 마 찬가 지 였 다.
코드 를 생 성 할 항목 에'이벤트 명령 줄 미리 생 성'을 추가 합 니 다.

"$(ProjectDir)..\CodeGenerator\$(OutDir)net47\CodeGenerator.exe" "$(ProjectDir)..\Walterlv.Demo\Generic\IDemoFile.cs" "$(ProjectDir)..\..\Walterlv.Demo\Generic\DemoFile.cs" 
이제 이 항목 을 컴 파일 하면 여러 개의 범 형 류 를 만 들 수 있 습 니 다.
달걀
GenericTypeGenerator 류 의 코드 를 자세히 읽 었 다 면,내 가 생 성 한 파일 에 조건 부 컴 파일 러'GENERATED'를 추가 한 것 을 알 게 될 것 입 니 다.CODE”。이렇게 하면 부분 을 바 꾸 거나 차이 가 있 는 코드 를 바 꾸 지 않 아 도 된다.
이때 코드 를 쓰 면 템 플 릿 을 쓰 고 있다 는 것 을 전혀 느끼 지 못 하지 않 습 니까?코드 착색 도 있 고 팀 의 다른 개발 자 들 의 개발 환경 에 도 적용 된다.예,개인 적 으로 편리 함 을 가 져 오 는 동시에 도구 의 존 재 를 알 아차 리 지 못 하면 이 도구 가 좋다 고 생각 합 니 다.
코드 파일 을 자동 으로 찾 는 것 으로 바 꾸 고 이 도 구 를 NuGet 에 발표 하면 NuGet 설치 스 크 립 트 를 통 해 이 과정 을 자동화 할 수 있 습 니 다.
참고 자료
  • Code Generation and T4 Text Templates - Microsoft Docs
  • Custom Tools - Microsoft Docs
  • 총결산
    이상 은 이 글 의 전체 내용 입 니 다.본 논문 의 내용 이 여러분 의 학습 이나 업무 에 어느 정도 참고 학습 가치 가 있 기 를 바 랍 니 다.궁금 한 점 이 있 으 시 면 댓 글 을 남 겨 주 셔 서 저희 에 대한 지지 에 감 사 드 립 니 다.

    좋은 웹페이지 즐겨찾기