2025-04-28 14:07:51 +08:00
|
|
|
|
using Microsoft.CodeAnalysis;
|
|
|
|
|
|
using Microsoft.CodeAnalysis.CSharp.Syntax;
|
2025-05-07 10:15:45 +08:00
|
|
|
|
using System;
|
2025-04-28 14:07:51 +08:00
|
|
|
|
using System.Collections.Generic;
|
|
|
|
|
|
using System.Diagnostics;
|
|
|
|
|
|
using System.Linq;
|
|
|
|
|
|
using System.Text;
|
|
|
|
|
|
|
|
|
|
|
|
namespace JiShe.CollectBus.IncrementalGenerator
|
|
|
|
|
|
{
|
|
|
|
|
|
/// <summary>
|
2025-05-07 10:15:45 +08:00
|
|
|
|
/// 复杂类型增量源生成器
|
2025-04-28 14:07:51 +08:00
|
|
|
|
/// </summary>
|
|
|
|
|
|
[Generator(LanguageNames.CSharp)]
|
2025-04-29 09:14:58 +08:00
|
|
|
|
public class ComplexTypeSourceAnalyzers : IIncrementalGenerator
|
2025-04-28 14:07:51 +08:00
|
|
|
|
{
|
2025-04-29 10:02:10 +08:00
|
|
|
|
private const string AttributeFullName = "JiShe.CollectBus.Analyzers.Shared.SourceAnalyzersAttribute";
|
2025-05-06 23:46:12 +08:00
|
|
|
|
private const string SourceEntityAccessorFactoryNamespace = "JiShe.CollectBus.Analyzers.Shared";
|
2025-04-28 14:07:51 +08:00
|
|
|
|
|
|
|
|
|
|
public void Initialize(IncrementalGeneratorInitializationContext context)
|
|
|
|
|
|
{
|
2025-04-28 22:35:19 +08:00
|
|
|
|
//Debugger.Launch();
|
2025-04-28 14:07:51 +08:00
|
|
|
|
|
|
|
|
|
|
|
2025-05-06 23:46:12 +08:00
|
|
|
|
// 步骤1:筛选带有 [SourceAnalyzers] 的类
|
2025-04-28 14:07:51 +08:00
|
|
|
|
var classDeclarations = context.SyntaxProvider
|
|
|
|
|
|
.CreateSyntaxProvider(
|
|
|
|
|
|
predicate: static (s, _) => IsClassWithAttribute(s),
|
|
|
|
|
|
transform: static (ctx, _) => GetClassDeclaration(ctx))
|
|
|
|
|
|
.Where(static c => c is not null);
|
|
|
|
|
|
|
|
|
|
|
|
// 步骤2:合并编译信息
|
|
|
|
|
|
var compilationAndClasses = context.CompilationProvider.Combine(classDeclarations.Collect());
|
|
|
|
|
|
|
|
|
|
|
|
context.RegisterSourceOutput(compilationAndClasses, (spc, source) =>
|
2025-05-07 10:15:45 +08:00
|
|
|
|
GenerateCode(source.Left, source.Right!, spc));
|
2025-04-28 14:07:51 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private static bool IsClassWithAttribute(SyntaxNode node) => node is ClassDeclarationSyntax cds && cds.AttributeLists.Count > 0;
|
2025-05-07 10:15:45 +08:00
|
|
|
|
|
2025-04-28 22:35:19 +08:00
|
|
|
|
private static ClassDeclarationSyntax GetClassDeclaration(GeneratorSyntaxContext context)
|
2025-04-28 14:07:51 +08:00
|
|
|
|
{
|
|
|
|
|
|
var classDecl = (ClassDeclarationSyntax)context.Node;
|
|
|
|
|
|
var attributeType = context.SemanticModel.Compilation.GetTypeByMetadataName(AttributeFullName);
|
|
|
|
|
|
|
|
|
|
|
|
foreach (var attribute in classDecl.AttributeLists.SelectMany(al => al.Attributes))
|
|
|
|
|
|
{
|
|
|
|
|
|
var symbol = context.SemanticModel.GetSymbolInfo(attribute).Symbol;
|
|
|
|
|
|
if (symbol is IMethodSymbol ctor &&
|
2025-04-28 16:37:31 +08:00
|
|
|
|
SymbolEqualityComparer.Default.Equals(ctor.ContainingType, attributeType))
|
2025-04-28 14:07:51 +08:00
|
|
|
|
{
|
|
|
|
|
|
return classDecl;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
return null;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-04-28 17:45:00 +08:00
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 递归获取所有层级的属性
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <param name="classSymbol"></param>
|
|
|
|
|
|
/// <returns></returns>
|
|
|
|
|
|
private static IEnumerable<IPropertySymbol> GetAllPropertiesInHierarchy(INamedTypeSymbol classSymbol)
|
|
|
|
|
|
{
|
|
|
|
|
|
var currentSymbol = classSymbol;
|
|
|
|
|
|
while (currentSymbol != null)
|
|
|
|
|
|
{
|
|
|
|
|
|
foreach (var prop in currentSymbol.GetMembers().OfType<IPropertySymbol>())
|
|
|
|
|
|
{
|
|
|
|
|
|
yield return prop;
|
|
|
|
|
|
}
|
|
|
|
|
|
currentSymbol = currentSymbol.BaseType; // 向上遍历基类
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-05-06 23:46:12 +08:00
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 生成代码
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <param name="compilation"></param>
|
|
|
|
|
|
/// <param name="classes"></param>
|
|
|
|
|
|
/// <param name="context"></param>
|
2025-04-28 14:07:51 +08:00
|
|
|
|
private static void GenerateCode(
|
|
|
|
|
|
Compilation compilation,
|
|
|
|
|
|
IEnumerable<ClassDeclarationSyntax> classes,
|
|
|
|
|
|
SourceProductionContext context)
|
|
|
|
|
|
{
|
|
|
|
|
|
var processedTypes = new HashSet<ITypeSymbol>(SymbolEqualityComparer.Default);
|
2025-05-06 23:46:12 +08:00
|
|
|
|
|
2025-04-28 16:37:31 +08:00
|
|
|
|
if (!classes.Any())
|
|
|
|
|
|
{
|
|
|
|
|
|
context.ReportDiagnostic(Diagnostic.Create(
|
2025-05-06 23:46:12 +08:00
|
|
|
|
new DiagnosticDescriptor("GEN002", "没有目标类",
|
|
|
|
|
|
"没有找到SourceAnalyzers标记的类", "Debug", DiagnosticSeverity.Warning, true),
|
2025-04-28 16:37:31 +08:00
|
|
|
|
Location.None));
|
|
|
|
|
|
}
|
2025-04-28 14:07:51 +08:00
|
|
|
|
|
|
|
|
|
|
foreach (var classDecl in classes.Distinct())
|
|
|
|
|
|
{
|
|
|
|
|
|
var model = compilation.GetSemanticModel(classDecl.SyntaxTree);
|
|
|
|
|
|
var classSymbol = model.GetDeclaredSymbol(classDecl) as INamedTypeSymbol;
|
|
|
|
|
|
|
2025-04-28 16:37:31 +08:00
|
|
|
|
if (classSymbol == null || !processedTypes.Add(classSymbol))
|
|
|
|
|
|
{
|
|
|
|
|
|
context.ReportDiagnostic(Diagnostic.Create(
|
2025-05-06 23:46:12 +08:00
|
|
|
|
new DiagnosticDescriptor("GEN003", "无效符号",
|
|
|
|
|
|
$"类名称为{classDecl.Identifier.Text} 符号为空", "Error", DiagnosticSeverity.Error, true),
|
2025-04-28 16:37:31 +08:00
|
|
|
|
Location.None));
|
|
|
|
|
|
continue;
|
|
|
|
|
|
}
|
2025-05-07 10:15:45 +08:00
|
|
|
|
|
|
|
|
|
|
var code = BuildAccessorsForType(classSymbol, compilation, processedTypes);
|
2025-04-28 16:37:31 +08:00
|
|
|
|
|
2025-04-28 22:35:19 +08:00
|
|
|
|
context.AddSource($"{classSymbol.Name}Extension.g.cs", code);
|
2025-05-06 23:46:12 +08:00
|
|
|
|
|
|
|
|
|
|
|
2025-05-07 10:15:45 +08:00
|
|
|
|
var code3 = BuildAccessorsForSourceEntity(classSymbol, compilation, processedTypes);
|
2025-05-06 23:46:12 +08:00
|
|
|
|
context.AddSource($"{classSymbol.Name}Accessor.g.cs", code3);
|
2025-04-28 14:07:51 +08:00
|
|
|
|
}
|
2025-05-06 23:46:12 +08:00
|
|
|
|
|
|
|
|
|
|
// 生成工厂注册代码
|
|
|
|
|
|
context.AddSource("SourceEntityAccessorFactory.g.cs", BuildFactoryCode());
|
2025-04-28 14:07:51 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2025-05-06 23:46:12 +08:00
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 构建类的属性访问器代码
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <param name="classSymbol"></param>
|
|
|
|
|
|
/// <param name="compilation"></param>
|
|
|
|
|
|
/// <param name="processedTypes"></param>
|
|
|
|
|
|
/// <returns></returns>
|
2025-04-28 14:07:51 +08:00
|
|
|
|
private static string BuildAccessorsForType(
|
2025-05-06 23:46:12 +08:00
|
|
|
|
INamedTypeSymbol classSymbol,
|
|
|
|
|
|
Compilation compilation,
|
|
|
|
|
|
HashSet<ITypeSymbol> processedTypes)
|
2025-04-28 14:07:51 +08:00
|
|
|
|
{
|
|
|
|
|
|
var code = new StringBuilder();
|
2025-04-28 16:37:31 +08:00
|
|
|
|
code.AppendLine("#pragma warning disable CS0419 // 禁用警告");
|
|
|
|
|
|
code.AppendLine("// Generated code for " + classSymbol.Name);
|
2025-04-28 14:07:51 +08:00
|
|
|
|
code.AppendLine("// <auto-generated/>");
|
|
|
|
|
|
code.AppendLine("#nullable enable");
|
|
|
|
|
|
code.AppendLine($"namespace {classSymbol.ContainingNamespace.ToDisplayString()};");
|
|
|
|
|
|
code.AppendLine();
|
|
|
|
|
|
|
|
|
|
|
|
code.AppendLine($"public static class {classSymbol.Name}Extension{GetGenericParams(classSymbol)}");
|
|
|
|
|
|
code.AppendLine("{");
|
|
|
|
|
|
|
2025-05-06 23:46:12 +08:00
|
|
|
|
foreach (var prop in GetAllPropertiesInHierarchy(classSymbol))
|
|
|
|
|
|
{
|
|
|
|
|
|
if (prop.IsIndexer) continue;
|
|
|
|
|
|
GeneratePropertyAccessorsForType(prop, code, compilation, processedTypes);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
code.AppendLine("}");
|
|
|
|
|
|
return code.ToString();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 获取泛型参数
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <param name="symbol"></param>
|
|
|
|
|
|
/// <returns></returns>
|
2025-04-28 16:37:31 +08:00
|
|
|
|
public static string GetGenericParams(INamedTypeSymbol symbol)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (!symbol.IsGenericType) return "";
|
|
|
|
|
|
var parameters = symbol.TypeParameters.Select(t => t.Name);
|
|
|
|
|
|
return $"<{string.Join(", ", parameters)}>";
|
|
|
|
|
|
}
|
2025-04-28 14:07:51 +08:00
|
|
|
|
|
2025-05-06 23:46:12 +08:00
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 生成属性访问器代码
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <param name="prop"></param>
|
|
|
|
|
|
/// <param name="code"></param>
|
|
|
|
|
|
/// <param name="compilation"></param>
|
|
|
|
|
|
/// <param name="processedTypes"></param>
|
|
|
|
|
|
private static void GeneratePropertyAccessorsForType(
|
2025-04-28 14:07:51 +08:00
|
|
|
|
IPropertySymbol prop,
|
|
|
|
|
|
StringBuilder code,
|
|
|
|
|
|
Compilation compilation,
|
|
|
|
|
|
HashSet<ITypeSymbol> processedTypes)
|
|
|
|
|
|
{
|
2025-05-06 23:46:12 +08:00
|
|
|
|
// 安全类型转换
|
2025-04-28 14:07:51 +08:00
|
|
|
|
if (prop.Type is not ITypeSymbol propType) return;
|
|
|
|
|
|
|
|
|
|
|
|
code.AppendLine($" // Processing property: {prop.Name}");
|
|
|
|
|
|
|
|
|
|
|
|
// 处理元组类型
|
|
|
|
|
|
if (propType is INamedTypeSymbol { IsTupleType: true } tupleType)
|
|
|
|
|
|
{
|
2025-05-07 10:15:45 +08:00
|
|
|
|
GenerateTupleAccessorsForType(prop, tupleType, code);
|
2025-04-28 14:07:51 +08:00
|
|
|
|
}
|
|
|
|
|
|
else if (propType is INamedTypeSymbol namedType)
|
|
|
|
|
|
{
|
2025-05-07 10:15:45 +08:00
|
|
|
|
GenerateStandardAccessorsForType(prop, namedType, code);
|
2025-04-28 14:07:51 +08:00
|
|
|
|
ProcessNestedType(namedType, compilation, processedTypes);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-05-06 23:46:12 +08:00
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 处理元组类型
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <param name="prop"></param>
|
|
|
|
|
|
/// <param name="tupleType"></param>
|
|
|
|
|
|
/// <param name="code"></param>
|
2025-05-07 10:15:45 +08:00
|
|
|
|
private static void GenerateTupleAccessorsForType(IPropertySymbol prop, INamedTypeSymbol tupleType, StringBuilder code)
|
2025-04-28 14:07:51 +08:00
|
|
|
|
{
|
|
|
|
|
|
var elements = tupleType.TupleElements;
|
|
|
|
|
|
var parentType = prop.ContainingType.ToDisplayString();
|
|
|
|
|
|
|
|
|
|
|
|
for (int i = 0; i < elements.Length; i++)
|
|
|
|
|
|
{
|
|
|
|
|
|
var element = elements[i];
|
|
|
|
|
|
if (element.Type is not ITypeSymbol elementType) continue;
|
|
|
|
|
|
|
|
|
|
|
|
var elementName = element.CorrespondingTupleField?.Name ?? $"Item{i + 1}";
|
|
|
|
|
|
code.AppendLine($" public static {elementType.ToDisplayString()} Get{prop.Name}_{elementName}({parentType} obj) => obj.{prop.Name}.{elementName};");
|
|
|
|
|
|
|
|
|
|
|
|
if (prop.SetMethod != null)
|
|
|
|
|
|
{
|
|
|
|
|
|
var assignments = elements.Select((e, idx) =>
|
|
|
|
|
|
idx == i ? "value" : $"obj.{prop.Name}.{e.Name}");
|
|
|
|
|
|
code.AppendLine($" public static void Set{prop.Name}_{elementName}({parentType} obj, {elementType.ToDisplayString()} value) => obj.{prop.Name} = ({string.Join(", ", assignments)});");
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-05-06 23:46:12 +08:00
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 生成标准属性的访问器
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <param name="prop"></param>
|
|
|
|
|
|
/// <param name="propType"></param>
|
|
|
|
|
|
/// <param name="code"></param>
|
2025-05-07 10:15:45 +08:00
|
|
|
|
private static void GenerateStandardAccessorsForType(IPropertySymbol prop, INamedTypeSymbol propType, StringBuilder code)
|
2025-04-28 14:07:51 +08:00
|
|
|
|
{
|
|
|
|
|
|
var parentType = prop.ContainingType.ToDisplayString();
|
|
|
|
|
|
code.AppendLine($" public static {propType.ToDisplayString()} Get{prop.Name}({parentType} obj) => obj.{prop.Name};");
|
|
|
|
|
|
|
|
|
|
|
|
if (prop.SetMethod != null)
|
|
|
|
|
|
{
|
|
|
|
|
|
code.AppendLine($" public static void Set{prop.Name}({parentType} obj, {propType.ToDisplayString()} value) => obj.{prop.Name} = value;");
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-05-07 10:15:45 +08:00
|
|
|
|
|
2025-05-06 23:46:12 +08:00
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 处理嵌套类型
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <param name="typeSymbol"></param>
|
|
|
|
|
|
/// <param name="compilation"></param>
|
|
|
|
|
|
/// <param name="processedTypes"></param>
|
2025-04-28 14:07:51 +08:00
|
|
|
|
private static void ProcessNestedType(ITypeSymbol typeSymbol, Compilation compilation, HashSet<ITypeSymbol> processedTypes)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (typeSymbol is not INamedTypeSymbol namedType) return;
|
|
|
|
|
|
if (!ShouldProcessNestedType(namedType)) return;
|
|
|
|
|
|
if (!processedTypes.Add(namedType)) return;
|
|
|
|
|
|
|
|
|
|
|
|
var code = BuildAccessorsForType(namedType, compilation, processedTypes);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-05-06 23:46:12 +08:00
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 处理嵌套类型
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <param name="symbol"></param>
|
|
|
|
|
|
/// <returns></returns>
|
2025-04-28 14:07:51 +08:00
|
|
|
|
private static bool ShouldProcessNestedType(INamedTypeSymbol symbol)
|
|
|
|
|
|
{
|
|
|
|
|
|
return symbol.DeclaredAccessibility == Accessibility.Public &&
|
|
|
|
|
|
!symbol.IsTupleType &&
|
|
|
|
|
|
!symbol.IsAnonymousType &&
|
|
|
|
|
|
!symbol.IsImplicitlyDeclared &&
|
|
|
|
|
|
!symbol.Name.StartsWith("<");
|
|
|
|
|
|
}
|
2025-05-06 23:46:12 +08:00
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 生成扩展类工厂代码
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <returns></returns>
|
|
|
|
|
|
private static string BuildFactoryCode()
|
|
|
|
|
|
{
|
|
|
|
|
|
return """
|
|
|
|
|
|
using System;
|
|
|
|
|
|
using System.Collections.Concurrent;
|
|
|
|
|
|
namespace JiShe.CollectBus.Analyzers.Shared;
|
|
|
|
|
|
public static class SourceEntityAccessorFactory
|
|
|
|
|
|
{
|
|
|
|
|
|
private static readonly ConcurrentDictionary<Type, object> _accessors = new();
|
|
|
|
|
|
|
|
|
|
|
|
public static ISourceEntityAccessor<T> GetAccessor<T>()
|
|
|
|
|
|
{
|
|
|
|
|
|
if (!_accessors.TryGetValue(typeof(T), out var accessor))
|
|
|
|
|
|
{
|
|
|
|
|
|
accessor = Activator.CreateInstance(
|
|
|
|
|
|
Type.GetType($"{typeof(T).Namespace}.{typeof(T).Name}Accessor")!)!;
|
|
|
|
|
|
_accessors.TryAdd(typeof(T), accessor);
|
|
|
|
|
|
}
|
|
|
|
|
|
return (ISourceEntityAccessor<T>)accessor!;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
""";
|
|
|
|
|
|
}
|
2025-05-07 10:15:45 +08:00
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 构建实体类的源属性访问器代码
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <param name="classSymbol"></param>
|
|
|
|
|
|
/// <param name="compilation"></param>
|
|
|
|
|
|
/// <param name="processedTypes"></param>
|
|
|
|
|
|
/// <returns></returns>
|
|
|
|
|
|
private static string BuildAccessorsForSourceEntity(
|
|
|
|
|
|
INamedTypeSymbol classSymbol,
|
|
|
|
|
|
Compilation compilation,
|
|
|
|
|
|
HashSet<ITypeSymbol> processedTypes)
|
|
|
|
|
|
{
|
|
|
|
|
|
var code = new StringBuilder();
|
|
|
|
|
|
code.AppendLine("#pragma warning disable CS0419 // 禁用警告");
|
|
|
|
|
|
code.AppendLine("// Generated code for " + classSymbol.Name);
|
|
|
|
|
|
code.AppendLine("// <auto-generated/>");
|
|
|
|
|
|
code.AppendLine("#nullable enable");
|
|
|
|
|
|
code.AppendLine("using System;");
|
|
|
|
|
|
code.AppendLine("using JiShe.CollectBus.Analyzers.Shared;");
|
|
|
|
|
|
code.AppendLine($"namespace {classSymbol.ContainingNamespace.ToDisplayString()};");
|
|
|
|
|
|
code.AppendLine();
|
|
|
|
|
|
|
|
|
|
|
|
code.AppendLine($"public sealed class {classSymbol.Name}Accessor : ISourceEntityAccessor<{classSymbol.Name}>");
|
|
|
|
|
|
code.AppendLine("{");
|
|
|
|
|
|
|
|
|
|
|
|
GetGeneratePropertyValueForSourceEntity(GetAllPropertiesInHierarchy(classSymbol), code, compilation, classSymbol);
|
|
|
|
|
|
|
|
|
|
|
|
SetGeneratePropertyValueForSourceEntity(GetAllPropertiesInHierarchy(classSymbol), code, compilation, classSymbol);
|
|
|
|
|
|
|
|
|
|
|
|
code.AppendLine("}");
|
|
|
|
|
|
|
|
|
|
|
|
return code.ToString();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private static void GetGeneratePropertyValueForSourceEntity(IEnumerable<IPropertySymbol> propList,
|
|
|
|
|
|
StringBuilder code,
|
|
|
|
|
|
Compilation compilation,
|
|
|
|
|
|
INamedTypeSymbol classSymbol)
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
|
|
|
|
code.AppendLine($" public object GetPropertyValue({classSymbol.Name} targetEntity, string propertyName)");
|
|
|
|
|
|
code.AppendLine(" {");
|
|
|
|
|
|
code.AppendLine(" return propertyName switch");
|
|
|
|
|
|
code.AppendLine(" {");
|
|
|
|
|
|
|
|
|
|
|
|
// 遍历所有属性
|
|
|
|
|
|
foreach (var property in propList)
|
|
|
|
|
|
{
|
|
|
|
|
|
// 安全类型转换
|
|
|
|
|
|
if (property.Type is not ITypeSymbol propType) continue;
|
|
|
|
|
|
|
|
|
|
|
|
// 处理元组类型
|
|
|
|
|
|
if (propType is INamedTypeSymbol { IsTupleType: true } tupleType)
|
|
|
|
|
|
{
|
|
|
|
|
|
}
|
|
|
|
|
|
else if (propType is INamedTypeSymbol namedType)
|
|
|
|
|
|
{
|
|
|
|
|
|
// 生成属性访问的
|
|
|
|
|
|
code.AppendLine($" \"{property.Name}\" => {classSymbol.Name}Extension.Get{property.Name}(targetEntity),");
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
code.AppendLine(" _ => throw new ArgumentException($\"Unknown property: {propertyName}\")");
|
|
|
|
|
|
code.AppendLine(" };");
|
|
|
|
|
|
code.AppendLine(" }");
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private static void SetGeneratePropertyValueForSourceEntity(IEnumerable<IPropertySymbol> propList,
|
|
|
|
|
|
StringBuilder code,
|
|
|
|
|
|
Compilation compilation,
|
|
|
|
|
|
INamedTypeSymbol classSymbol)
|
|
|
|
|
|
{
|
|
|
|
|
|
code.AppendLine($" public void SetPropertyValue({classSymbol.Name} targetEntity, string propertyName, object value)");
|
|
|
|
|
|
code.AppendLine(" {");
|
|
|
|
|
|
code.AppendLine(" return propertyName switch");
|
|
|
|
|
|
code.AppendLine(" {");
|
|
|
|
|
|
|
|
|
|
|
|
// 遍历所有属性
|
|
|
|
|
|
foreach (var property in propList)
|
|
|
|
|
|
{
|
|
|
|
|
|
// 安全类型转换
|
|
|
|
|
|
if (property.Type is not ITypeSymbol propType) continue;
|
|
|
|
|
|
|
|
|
|
|
|
// 处理元组类型
|
|
|
|
|
|
if (propType is INamedTypeSymbol { IsTupleType: true } tupleType)
|
|
|
|
|
|
{
|
|
|
|
|
|
}
|
|
|
|
|
|
else if (propType is INamedTypeSymbol namedType)
|
|
|
|
|
|
{
|
|
|
|
|
|
// 生成属性设置赋值
|
|
|
|
|
|
code.AppendLine($" \"{property.Name}\" => {classSymbol.Name}Extension.Set{property.Name}(targetEntity,({namedType.ToDisplayString()})value),");
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
code.AppendLine(" _ => throw new ArgumentException($\"Unknown property: {propertyName}\")");
|
|
|
|
|
|
code.AppendLine(" };");
|
|
|
|
|
|
code.AppendLine(" }");
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 处理元组类型
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <param name="prop"></param>
|
|
|
|
|
|
/// <param name="tupleType"></param>
|
|
|
|
|
|
/// <param name="code"></param>
|
|
|
|
|
|
private static void GenerateTupleAccessorsForSourceEntity(IPropertySymbol prop, INamedTypeSymbol tupleType, StringBuilder code)
|
|
|
|
|
|
{
|
|
|
|
|
|
var elements = tupleType.TupleElements;
|
|
|
|
|
|
var parentType = prop.ContainingType.ToDisplayString();
|
|
|
|
|
|
|
|
|
|
|
|
for (int i = 0; i < elements.Length; i++)
|
|
|
|
|
|
{
|
|
|
|
|
|
var element = elements[i];
|
|
|
|
|
|
if (element.Type is not ITypeSymbol elementType) continue;
|
|
|
|
|
|
|
|
|
|
|
|
var elementName = element.CorrespondingTupleField?.Name ?? $"Item{i + 1}";
|
|
|
|
|
|
code.AppendLine($" public static {elementType.ToDisplayString()} Get{prop.Name}_{elementName}({parentType} obj) => obj.{prop.Name}.{elementName};");
|
|
|
|
|
|
|
|
|
|
|
|
if (prop.SetMethod != null)
|
|
|
|
|
|
{
|
|
|
|
|
|
var assignments = elements.Select((e, idx) =>
|
|
|
|
|
|
idx == i ? "value" : $"obj.{prop.Name}.{e.Name}");
|
|
|
|
|
|
code.AppendLine($" public static void Set{prop.Name}_{elementName}({parentType} obj, {elementType.ToDisplayString()} value) => obj.{prop.Name} = ({string.Join(", ", assignments)});");
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 生成标准属性的访问器
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <param name="prop"></param>
|
|
|
|
|
|
/// <param name="propType"></param>
|
|
|
|
|
|
/// <param name="code"></param>
|
|
|
|
|
|
private static void GenerateStandardAccessorsForSourceEntity(IPropertySymbol prop, INamedTypeSymbol propType, StringBuilder code)
|
|
|
|
|
|
{
|
|
|
|
|
|
var parentType = prop.ContainingType.ToDisplayString();
|
|
|
|
|
|
code.AppendLine($" public object {propType.ToDisplayString()} Get{prop.Name}({parentType} obj) => obj.{prop.Name};");
|
|
|
|
|
|
|
|
|
|
|
|
if (prop.SetMethod != null)
|
|
|
|
|
|
{
|
|
|
|
|
|
code.AppendLine($" public static void Set{prop.Name}({parentType} obj, {propType.ToDisplayString()} value) => obj.{prop.Name} = value;");
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2025-04-28 14:07:51 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|