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 설치 스 크 립 트 를 통 해 이 과정 을 자동화 할 수 있 습 니 다.
참고 자료
이상 은 이 글 의 전체 내용 입 니 다.본 논문 의 내용 이 여러분 의 학습 이나 업무 에 어느 정도 참고 학습 가치 가 있 기 를 바 랍 니 다.궁금 한 점 이 있 으 시 면 댓 글 을 남 겨 주 셔 서 저희 에 대한 지지 에 감 사 드 립 니 다.
이 내용에 흥미가 있습니까?
현재 기사가 여러분의 문제를 해결하지 못하는 경우 AI 엔진은 머신러닝 분석(스마트 모델이 방금 만들어져 부정확한 경우가 있을 수 있음)을 통해 가장 유사한 기사를 추천합니다:
iOS는 앱을 파일과 연결합니다.특정 문서 유형을 처리하도록 응용 프로그램을 등록할 수 있으며 문서 컨트롤러를 사용하는 모든 응용 프로그램은 이러한 문서의 처리를 사용자에게 넘길 수 있습니다. 예를 들어, 내 응용 프로그램 Molecules(소스 ...
텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
CC BY-SA 2.5, CC BY-SA 3.0 및 CC BY-SA 4.0에 따라 라이센스가 부여됩니다.