XFE Git
XFE Studio Git
Git 首页 全局搜索
XFE 主站 文档 NuGet

XFEExtension.NetCore.AutoImplement

【DLL】自动生成实现类

公开
关注 0 Fork 0 Star 0
返回提交历史

XFEstudio/XFEExtension.NetCore.AutoImplement

现在,可以通过在CreateImpl特性的传参中设置属性来实现自定义实现类的类名、命名空间及可访问性修饰符 且现在的实现类默认使用`internal`和`partial`修饰符,可以更加方便的修改实现类中的内容

f81581c
XFE工作室室长 <mail@xfegzs.com>
提交于

代码差异

4 个文件 +137 -68
Modified XFEExtension.NetCore.AutoImplement.Analyzer/Generator/ImplementAutoGenerator.cs +126 -63
@@ -1,11 +1,8 @@
1 1 using Microsoft.CodeAnalysis;
2 2 using Microsoft.CodeAnalysis.CSharp;
3 3 using Microsoft.CodeAnalysis.CSharp.Syntax;
4 using System;
5 using System.Diagnostics;
4 using System.Collections.Generic;
6 5 using System.Linq;
7 using System.Threading;
8 using XFEExtension.NetCore.AutoImplement.Analyzer.Model;
9 6
10 7 namespace XFEExtension.NetCore.AutoImplement.Analyzer.Generator;
11 8
@@ -17,69 +14,135 @@ public class ImplementAutoGenerator : IIncrementalGenerator
17 14 //var implementationProvider = context.SyntaxProvider.ForAttributeWithMetadataName("CreateImpl",
18 15 //static (x, _) => x is ClassDeclarationSyntax classDeclaration && classDeclaration.AttributeLists.Any(IsCreateImplAttribute),
19 16 //GetCreateImpl);
20 var implementationProvider = context.SyntaxProvider.ForAttributeWithMetadataName("CreateImpl",
21 static (x, _) => x is ClassDeclarationSyntax classDeclaration,
22 GetCreateImpl);
23 context.RegisterSourceOutput(implementationProvider.Combine(context.CompilationProvider), Generate);
17 //context.RegisterSourceOutput(implementationProvider.Combine(context.CompilationProvider), Generate);
18 context.RegisterSourceOutput(context.CompilationProvider, Generate);
24 19 }
25 20
26 public void Generate(SourceProductionContext context, (ImplementationInfo? ImplementationInfo, Compilation Compilation) args)
21 //public void Generate(SourceProductionContext context, (ImplementationInfo? ImplementationInfo, Compilation Compilation) args)
22 //{
23 // if (args.ImplementationInfo is null)
24 // return;
25 // var root = args.ImplementationInfo.ClassDeclaration.SyntaxTree.GetRoot();
26 // var usingDirectives = root.DescendantNodes().OfType<UsingDirectiveSyntax>().ToArray();
27 // FileScopedNamespaceDeclarationSyntax? fileScopedNamespaceDeclarationSyntax = null;
28 // var namespaceResults = root.DescendantNodes().OfType<FileScopedNamespaceDeclarationSyntax>();
29 // if (namespaceResults != null && namespaceResults.Count() > 0)
30 // fileScopedNamespaceDeclarationSyntax = namespaceResults.First();
31 // var implementationSyntaxTree = GenerateImplementationSyntaxTree(args.ImplementationInfo.ClassDeclaration, usingDirectives, fileScopedNamespaceDeclarationSyntax, args.ImplementationInfo.TargetClassName, args.ImplementationInfo.Modifiers, args.ImplementationInfo.NameSpace);
32 // context.AddSource($"{args.ImplementationInfo.TargetClassName}.g.cs", implementationSyntaxTree.ToString());
33 //}
34
35 public void Generate(SourceProductionContext context, Compilation compilation)
27 36 {
28 if (args.ImplementationInfo is null)
29 return;
30 var root = args.ImplementationInfo.ClassDeclaration.SyntaxTree.GetRoot();
31 var usingDirectives = root.DescendantNodes().OfType<UsingDirectiveSyntax>().ToArray();
32 FileScopedNamespaceDeclarationSyntax? fileScopedNamespaceDeclarationSyntax = null;
33 var namespaceResults = root.DescendantNodes().OfType<FileScopedNamespaceDeclarationSyntax>();
34 if (namespaceResults != null && namespaceResults.Count() > 0)
35 fileScopedNamespaceDeclarationSyntax = namespaceResults.First();
36 var implementationSyntaxTree = GenerateImplementationSyntaxTree(args.ImplementationInfo.ClassDeclaration, usingDirectives, fileScopedNamespaceDeclarationSyntax, args.ImplementationInfo.TargetClassName, args.ImplementationInfo.Modifiers, args.ImplementationInfo.NameSpace);
37 context.AddSource($"{args.ImplementationInfo.TargetClassName}.g.cs", implementationSyntaxTree.ToString());
37 var syntaxTrees = compilation.SyntaxTrees;
38 foreach (var syntaxTree in syntaxTrees)
39 {
40 var root = syntaxTree.GetRoot();
41 var classDeclarations = root.DescendantNodes().OfType<ClassDeclarationSyntax>()
42 .Where(classDeclaration => classDeclaration.AttributeLists.Any(IsCreateImplAttribute));
43 var usingDirectives = root.DescendantNodes().OfType<UsingDirectiveSyntax>().ToArray();
44 FileScopedNamespaceDeclarationSyntax? fileScopedNamespaceDeclarationSyntax = null;
45 var namespaceResults = root.DescendantNodes().OfType<FileScopedNamespaceDeclarationSyntax>();
46 if (namespaceResults != null && namespaceResults.Count() > 0)
47 fileScopedNamespaceDeclarationSyntax = namespaceResults.First();
48 foreach (var classDeclaration in classDeclarations)
49 {
50 foreach (var attribute in GetCreateImplAttributeList(classDeclaration))
51 {
52 var className = $"{classDeclaration.Identifier.ValueText}Impl";
53 var modifiers = new string[] { "internal", "partial" };
54 var nameSpace = string.Empty;
55 if (attribute.ArgumentList is not null && attribute.ArgumentList.Arguments.Count > 0)
56 {
57 foreach (var argument in attribute.ArgumentList.Arguments)
58 {
59 if (argument.Expression is LiteralExpressionSyntax literalExpressionSyntax && literalExpressionSyntax.IsKind(SyntaxKind.StringLiteralExpression))
60 {
61 className = literalExpressionSyntax.Token.ValueText;
62 }
63 else if (argument.NameEquals is NameEqualsSyntax nameEqualsSyntax)
64 {
65 var argumentName = nameEqualsSyntax.Name.Identifier.ValueText;
66 if (argumentName == "ClassName" && argument.Expression is LiteralExpressionSyntax classNameLiteral && classNameLiteral.IsKind(SyntaxKind.StringLiteralExpression))
67 {
68 className = classNameLiteral.Token.ValueText;
69 }
70 else if (argumentName == "Modifiers")
71 {
72 if (argument.Expression is ArrayCreationExpressionSyntax arrayCreationExpressionSyntax)
73 {
74 if (arrayCreationExpressionSyntax.Initializer is InitializerExpressionSyntax initializerExpressionSyntax)
75 {
76 modifiers = [.. initializerExpressionSyntax.Expressions.OfType<LiteralExpressionSyntax>()
77 .Where(literal => literal.IsKind(SyntaxKind.StringLiteralExpression))
78 .Select(literal => literal.Token.ValueText)
79 .Where(s => !string.IsNullOrEmpty(s))];
80 }
81 }
82 else if (argument.Expression is CollectionExpressionSyntax collectionExpressionSyntax)
83 {
84 modifiers = [.. collectionExpressionSyntax.ChildNodes().OfType<ExpressionElementSyntax>()
85 .Select(element => element.Expression.ToString().Replace("\"",""))
86 .Where(s => !string.IsNullOrEmpty(s))];
87 }
88 }
89 else if (argumentName == "NameSpace" && argument.Expression is LiteralExpressionSyntax nameSpaceLiteral && nameSpaceLiteral.IsKind(SyntaxKind.StringLiteralExpression))
90 {
91 nameSpace = nameSpaceLiteral.Token.ValueText;
92 }
93 }
94 }
95 }
96 var implementationSyntaxTree = GenerateImplementationSyntaxTree(classDeclaration, usingDirectives, fileScopedNamespaceDeclarationSyntax, className, modifiers, nameSpace);
97 context.AddSource($"{className}.g.cs", implementationSyntaxTree.ToString());
98 }
99 }
100 }
38 101 }
39 102
40 103 private static bool IsCreateImplAttribute(AttributeListSyntax attributeList) => attributeList.Attributes.Any(attribute => attribute.Name.ToString() == "CreateImpl");
41 104
42 //public static List<AttributeSyntax> GetCreateImplAttributeList(ClassDeclarationSyntax classDeclaration) => [.. classDeclaration.AttributeLists.Where(IsCreateImplAttribute).SelectMany(attributeList => attributeList.Attributes)];
105 public static List<AttributeSyntax> GetCreateImplAttributeList(ClassDeclarationSyntax classDeclaration) => [.. classDeclaration.AttributeLists.Where(IsCreateImplAttribute).SelectMany(attributeList => attributeList.Attributes)];
43 106
44 public static ImplementationInfo? GetCreateImpl(GeneratorAttributeSyntaxContext context, CancellationToken token)
45 {
46 if (context is { TargetSymbol: INamedTypeSymbol } && context.Attributes.First().ConstructorArguments.Length > 0)
47 Debugger.Launch();
48 token.ThrowIfCancellationRequested();
49 var classDeclaration = (ClassDeclarationSyntax)context.TargetNode;
50 var attributeData = context.Attributes.FirstOrDefault(ad => ad.AttributeClass?.ToDisplayString() == "CreateImpl");
51 if (attributeData is null)
52 return null;
53 var className = classDeclaration.Identifier.ValueText;
54 var targetClassName = attributeData.NamedArguments.FirstOrDefault(kv => kv.Key == "ClassName").Value.Value as string ?? $"{className}Impl";
55 //var targetClassName = $"{className}Impl";
56 var nameSpace = attributeData.NamedArguments.FirstOrDefault(kv => kv.Key == "NameSpace").Value.Value as string ?? string.Empty;
57 //var nameSpace = string.Empty;
58 //var value = attributeData.NamedArguments.FirstOrDefault(kv => kv.Key == "Modifiers").Value;
59 //var modifiers = attributeData.NamedArguments.FirstOrDefault(kv => kv.Key == "Modifiers").Value.Values.Select(v => v.Value?.ToString() ?? string.Empty).Where(s => !string.IsNullOrEmpty(s)).ToArray() ?? [];
60 var modifiers = Array.Empty<string>();
61 //foreach (var attributeList in classDeclaration.AttributeLists)
62 //{
63 // foreach (var attribute in attributeList.Attributes)
64 // {
65 // if (attribute is null || attribute.Name.ToString() != "CreateImpl")
66 // continue;
67 // foreach (var argument in attribute.ArgumentList.Arguments)
68 // {
107 //public static ImplementationInfo? GetCreateImpl(GeneratorAttributeSyntaxContext context, CancellationToken token)
108 //{
109 // if (context is { TargetSymbol: INamedTypeSymbol })
110 // Debugger.Launch();
111 // token.ThrowIfCancellationRequested();
112 // var classDeclaration = (ClassDeclarationSyntax)context.TargetNode;
113 // var attributeData = context.Attributes.FirstOrDefault(ad => ad.AttributeClass?.ToDisplayString() == "CreateImpl");
114 // if (attributeData is null)
115 // return null;
116 // var className = classDeclaration.Identifier.ValueText;
117 // var targetClassName = attributeData.NamedArguments.FirstOrDefault(kv => kv.Key == "ClassName").Value.Value as string ?? $"{className}Impl";
118 // //var targetClassName = $"{className}Impl";
119 // var nameSpace = attributeData.NamedArguments.FirstOrDefault(kv => kv.Key == "NameSpace").Value.Value as string ?? string.Empty;
120 // //var nameSpace = string.Empty;
121 // //var value = attributeData.NamedArguments.FirstOrDefault(kv => kv.Key == "Modifiers").Value;
122 // //var modifiers = attributeData.NamedArguments.FirstOrDefault(kv => kv.Key == "Modifiers").Value.Values.Select(v => v.Value?.ToString() ?? string.Empty).Where(s => !string.IsNullOrEmpty(s)).ToArray() ?? [];
123 // var modifiers = Array.Empty<string>();
124 // //foreach (var attributeList in classDeclaration.AttributeLists)
125 // //{
126 // // foreach (var attribute in attributeList.Attributes)
127 // // {
128 // // if (attribute is null || attribute.Name.ToString() != "CreateImpl")
129 // // continue;
130 // // foreach (var argument in attribute.ArgumentList.Arguments)
131 // // {
69 132
70 // }
71 // }
72 //}
73 if (modifiers.Length == 0)
74 modifiers = ["internal", "partial"];
75 return new ImplementationInfo
76 {
77 ClassDeclaration = classDeclaration,
78 TargetClassName = targetClassName,
79 Modifiers = modifiers,
80 NameSpace = nameSpace
81 };
82 }
133 // // }
134 // // }
135 // //}
136 // if (modifiers.Length == 0)
137 // modifiers = ["internal", "partial"];
138 // return new ImplementationInfo
139 // {
140 // ClassDeclaration = classDeclaration,
141 // TargetClassName = targetClassName,
142 // Modifiers = modifiers,
143 // NameSpace = nameSpace
144 // };
145 //}
83 146
84 147 private static SyntaxTree GenerateImplementationSyntaxTree(ClassDeclarationSyntax classDeclaration, UsingDirectiveSyntax[] usingDirectiveSyntaxes, FileScopedNamespaceDeclarationSyntax? fileScopedNamespaceDeclarationSyntax, string className, string[] modifierParameters, string nameSpace)
85 148 {
@@ -106,15 +169,15 @@ public class ImplementAutoGenerator : IIncrementalGenerator
106 169 }
107 170 else
108 171 {
109 implementationClass = SyntaxFactory.ClassDeclaration($"{className}Impl")
172 implementationClass = SyntaxFactory.ClassDeclaration(className)
110 173 .AddModifiers(modifiers)
111 .AddMembers(SyntaxFactory.ConstructorDeclaration($"{className}Impl")
174 .AddMembers(SyntaxFactory.ConstructorDeclaration(className)
112 175 .AddModifiers(SyntaxFactory.Token(SyntaxKind.InternalKeyword))
113 176 .WithBody(SyntaxFactory.Block())
114 177 .WithParameterList(classDeclaration.ParameterList)
115 178 .WithInitializer(SyntaxFactory.ConstructorInitializer(SyntaxKind.BaseConstructorInitializer, SyntaxFactory.ArgumentList(SyntaxFactory.SeparatedList(classDeclaration.ParameterList.Parameters.Select(parameter => SyntaxFactory.Argument(SyntaxFactory.IdentifierName(parameter.Identifier))))))))
116 179 .WithLeadingTrivia(SyntaxFactory.ParseLeadingTrivia($@"/// <summary>
117 /// <seealso cref=""{className}Impl""/> 是根据 <seealso cref=""{className}""/> 自动生成的实现类
180 /// <seealso cref=""{className}""/> 是根据 <seealso cref=""{fatherClassName}""/> 自动生成的实现类
118 181 /// </summary>
119 182 "))
120 183 .NormalizeWhitespace();
@@ -122,12 +185,12 @@ public class ImplementAutoGenerator : IIncrementalGenerator
122 185 if (classDeclaration.TypeParameterList is TypeParameterListSyntax typeParameterListSyntax && typeParameterListSyntax.Parameters.Count > 0)
123 186 {
124 187 implementationClass = implementationClass.AddTypeParameterListParameters([.. typeParameterListSyntax.Parameters])
125 .AddBaseListTypes(SyntaxFactory.SimpleBaseType(SyntaxFactory.ParseTypeName($"{className}{typeParameterListSyntax}")))
188 .AddBaseListTypes(SyntaxFactory.SimpleBaseType(SyntaxFactory.ParseTypeName($"{fatherClassName}{typeParameterListSyntax}")))
126 189 .AddConstraintClauses([.. classDeclaration.ConstraintClauses]);
127 190 }
128 191 else
129 192 {
130 implementationClass = implementationClass.AddBaseListTypes(SyntaxFactory.SimpleBaseType(SyntaxFactory.ParseTypeName(className)));
193 implementationClass = implementationClass.AddBaseListTypes(SyntaxFactory.SimpleBaseType(SyntaxFactory.ParseTypeName(fatherClassName)));
131 194 }
132 195 MemberDeclarationSyntax memberDeclaration;
133 196 if (nameSpace != string.Empty)
Modified XFEExtension.NetCore.AutoImplement.Test/MyTestClass.cs +1 -1
@@ -2,7 +2,7 @@
2 2
3 3 [CreateImpl("MyTestClassA")]
4 4 [CreateImpl("MyTestClassB", Modifiers = ["public", "partial"])]
5 [CreateImpl("MyTestClassC", Modifiers = ["public", "partial"])]
5 [CreateImpl("MyTestClassC", Modifiers = new string[] { "public", "partial" })]
6 6 public abstract class MyTestClass<T, F>(T obj, F obj2) where T : class where F : new()
7 7 {
8 8 public T TProperty { get; set; } = obj;
Modified XFEExtension.NetCore.AutoImplement.Test/Program.cs +5 -1
@@ -1,2 +1,6 @@
1 1 // See https://aka.ms/new-console-template for more information
2 Console.WriteLine("Hello, World!");
2 using XFEExtension.NetCore.AutoImplement.Test;
3
4 Console.WriteLine("Hello, World!");
5
6 var myClass = new MyTestClassC();
Modified XFEExtension.NetCore.AutoImplement/XFEExtension.NetCore.AutoImplement.csproj +5 -3
@@ -5,7 +5,7 @@
5 5 <ImplicitUsings>enable</ImplicitUsings>
6 6 <Nullable>enable</Nullable>
7 7 <GeneratePackageOnBuild>True</GeneratePackageOnBuild>
8 <Version>1.0.1</Version>
8 <Version>1.1.0</Version>
9 9 <Title>AutoImplement</Title>
10 10 <Authors>XFEstudio</Authors>
11 11 <Company>寰宇朽力网络科技有限公司</Company>
@@ -16,8 +16,10 @@
16 16 <PackageReadmeFile>README.md</PackageReadmeFile>
17 17 <RepositoryUrl>https://github.com/XFEstudio/XFEExtension.NetCore.AutoImplement.git</RepositoryUrl>
18 18 <PackageTags>XFE;impl;implement;auto</PackageTags>
19 <PackageReleaseNotes>修复了不能实现泛型类的bug
20 修复了泛型类无法继承泛型约束条件的bug</PackageReleaseNotes>
19 <PackageReleaseNotes>
20 现在,可以通过在CreateImpl特性的传参中设置属性来实现自定义实现类的类名、命名空间及可访问性修饰符
21 且现在的实现类默认使用`internal`和`partial`修饰符,可以更加方便的修改实现类中的内容
22 </PackageReleaseNotes>
21 23 <PackageLicenseFile>LICENSE.txt</PackageLicenseFile>
22 24 <PackageRequireLicenseAcceptance>True</PackageRequireLicenseAcceptance>
23 25 </PropertyGroup>