mirror of
https://github.com/GeWuYou/GFramework.git
synced 2026-03-22 10:34:30 +08:00
refactor(ioc): 优化依赖注入容器的优先级排序实现
- 修改 MicrosoftDiContainer 中 GetAllByPriority 方法的排序逻辑,使用 OrderBy 确保稳定排序 - 修正注释中的中文描述,明确优先级排序规则 - 将 PriorityUsageAnalyzer 中的硬编码诊断规则替换为统一的 PriorityDiagnostic - 在 PriorityGenerator 中添加对嵌套类的支持检查,报告 GF_Priority_005 错误 - 改进生成文件的命名策略,使用完整元数据名称避免冲突 - 更新 AnalyzerReleases.Unshipped.md 文档,添加新的诊断规则说明 - 移除 PriorityGeneratorSnapshotTests 中关于嵌套类的测试用例
This commit is contained in:
parent
330bd8b0b0
commit
16d8cad4f3
@ -610,7 +610,7 @@ public class MicrosoftDiContainer(IServiceCollection? serviceCollection = null)
|
|||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 获取指定类型的所有实例,并按优先级排序
|
/// 获取指定类型的所有实例,并按优先级排序
|
||||||
/// 实现 IPrioritized 接口的服务将按值越小优先级越高)
|
/// 实现 IPrioritized 接口的服务将按优先级排序(数值越小优先级越高)
|
||||||
/// 未实现 IPrioritized 的服务将使用默认优先级 0
|
/// 未实现 IPrioritized 的服务将使用默认优先级 0
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="type">期望获取的实例类型</param>
|
/// <param name="type">期望获取的实例类型</param>
|
||||||
@ -632,17 +632,16 @@ public class MicrosoftDiContainer(IServiceCollection? serviceCollection = null)
|
|||||||
if (services.Count <= 1)
|
if (services.Count <= 1)
|
||||||
return services;
|
return services;
|
||||||
|
|
||||||
var list = services.ToList();
|
// 使用 OrderBy 确保稳定排序(相同优先级保持原有顺序)
|
||||||
|
return services
|
||||||
// 稳定排序:相同优先级保持注册顺序
|
.Select((service, index) => new { Service = service, Index = index })
|
||||||
list.Sort((a, b) =>
|
.OrderBy(x =>
|
||||||
{
|
{
|
||||||
var priorityA = a is IPrioritized pa ? pa.Priority : 0;
|
var priority = x.Service is IPrioritized p ? p.Priority : 0;
|
||||||
var priorityB = b is IPrioritized pb ? pb.Priority : 0;
|
return (priority, x.Index); // 先按优先级,再按索引
|
||||||
return priorityA.CompareTo(priorityB); // 升序
|
})
|
||||||
});
|
.Select(x => x.Service)
|
||||||
|
.ToList();
|
||||||
return list;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|||||||
@ -212,55 +212,4 @@ public class PriorityGeneratorSnapshotTests
|
|||||||
"PriorityGenerator",
|
"PriorityGenerator",
|
||||||
"GenericClass"));
|
"GenericClass"));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 测试嵌套类支持
|
|
||||||
/// </summary>
|
|
||||||
[Test]
|
|
||||||
public async Task Snapshot_NestedClass()
|
|
||||||
{
|
|
||||||
const string source = """
|
|
||||||
using System;
|
|
||||||
|
|
||||||
namespace GFramework.SourceGenerators.Abstractions.bases
|
|
||||||
{
|
|
||||||
[AttributeUsage(AttributeTargets.Class, Inherited = false, AllowMultiple = false)]
|
|
||||||
public sealed class PriorityAttribute : Attribute
|
|
||||||
{
|
|
||||||
public int Value { get; }
|
|
||||||
public PriorityAttribute(int value) { Value = value; }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
namespace GFramework.Core.Abstractions.bases
|
|
||||||
{
|
|
||||||
public interface IPrioritized
|
|
||||||
{
|
|
||||||
int Priority { get; }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
namespace TestApp
|
|
||||||
{
|
|
||||||
using GFramework.SourceGenerators.Abstractions.bases;
|
|
||||||
|
|
||||||
public class OuterClass
|
|
||||||
{
|
|
||||||
[Priority(30)]
|
|
||||||
public partial class NestedSystem
|
|
||||||
{
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
""";
|
|
||||||
|
|
||||||
await GeneratorSnapshotTest<PriorityGenerator>.RunAsync(
|
|
||||||
source,
|
|
||||||
Path.Combine(
|
|
||||||
TestContext.CurrentContext.TestDirectory,
|
|
||||||
"bases",
|
|
||||||
"snapshots",
|
|
||||||
"PriorityGenerator",
|
|
||||||
"NestedClass"));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@ -11,4 +11,5 @@
|
|||||||
GF_Priority_002 | GFramework.Priority | Warning | PriorityDiagnostic
|
GF_Priority_002 | GFramework.Priority | Warning | PriorityDiagnostic
|
||||||
GF_Priority_003 | GFramework.Priority | Error | PriorityDiagnostic
|
GF_Priority_003 | GFramework.Priority | Error | PriorityDiagnostic
|
||||||
GF_Priority_004 | GFramework.Priority | Error | PriorityDiagnostic
|
GF_Priority_004 | GFramework.Priority | Error | PriorityDiagnostic
|
||||||
|
GF_Priority_005 | GFramework.Priority | Error | PriorityDiagnostic
|
||||||
GF_Priority_Usage_001 | GFramework.Usage | Info | PriorityUsageAnalyzer
|
GF_Priority_Usage_001 | GFramework.Usage | Info | PriorityUsageAnalyzer
|
||||||
@ -1,4 +1,5 @@
|
|||||||
using System.Collections.Immutable;
|
using System.Collections.Immutable;
|
||||||
|
using GFramework.SourceGenerators.diagnostics;
|
||||||
using Microsoft.CodeAnalysis;
|
using Microsoft.CodeAnalysis;
|
||||||
using Microsoft.CodeAnalysis.Diagnostics;
|
using Microsoft.CodeAnalysis.Diagnostics;
|
||||||
using Microsoft.CodeAnalysis.Operations;
|
using Microsoft.CodeAnalysis.Operations;
|
||||||
@ -11,27 +12,11 @@ namespace GFramework.SourceGenerators.analyzers;
|
|||||||
[DiagnosticAnalyzer(LanguageNames.CSharp)]
|
[DiagnosticAnalyzer(LanguageNames.CSharp)]
|
||||||
public sealed class PriorityUsageAnalyzer : DiagnosticAnalyzer
|
public sealed class PriorityUsageAnalyzer : DiagnosticAnalyzer
|
||||||
{
|
{
|
||||||
/// <summary>
|
|
||||||
/// 诊断 ID
|
|
||||||
/// </summary>
|
|
||||||
private const string DiagnosticId = "GF_Priority_Usage_001";
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 诊断规则
|
|
||||||
/// </summary>
|
|
||||||
private static readonly DiagnosticDescriptor Rule = new(
|
|
||||||
id: DiagnosticId,
|
|
||||||
title: "建议使用 GetAllByPriority",
|
|
||||||
messageFormat: "类型 '{0}' 实现了 IPrioritized 接口,建议使用 GetAllByPriority<{0}>() 而非 GetAll<{0}>()",
|
|
||||||
category: "GFramework.Usage",
|
|
||||||
defaultSeverity: DiagnosticSeverity.Info,
|
|
||||||
isEnabledByDefault: true,
|
|
||||||
description: "当获取实现了 IPrioritized 接口的服务时,应使用 GetAllByPriority 方法以确保按优先级排序。");
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 支持的诊断规则
|
/// 支持的诊断规则
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics => ImmutableArray.Create(Rule);
|
public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics =>
|
||||||
|
ImmutableArray.Create(PriorityDiagnostic.SuggestGetAllByPriority);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 初始化分析器
|
/// 初始化分析器
|
||||||
@ -112,7 +97,7 @@ public sealed class PriorityUsageAnalyzer : DiagnosticAnalyzer
|
|||||||
|
|
||||||
// 报告诊断
|
// 报告诊断
|
||||||
var diagnostic = Diagnostic.Create(
|
var diagnostic = Diagnostic.Create(
|
||||||
Rule,
|
PriorityDiagnostic.SuggestGetAllByPriority,
|
||||||
invocation.Syntax.GetLocation(),
|
invocation.Syntax.GetLocation(),
|
||||||
typeArgument.ToDisplayString());
|
typeArgument.ToDisplayString());
|
||||||
|
|
||||||
|
|||||||
@ -45,8 +45,18 @@ public sealed class PriorityGenerator : MetadataAttributeClassGeneratorBase
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 2. 必须是 partial
|
// 2. 不支持嵌套类
|
||||||
if (!syntax.Modifiers.Any(SyntaxKind.PartialKeyword))
|
if (symbol.ContainingType != null)
|
||||||
|
{
|
||||||
|
context.ReportDiagnostic(Diagnostic.Create(
|
||||||
|
PriorityDiagnostic.NestedClassNotSupported,
|
||||||
|
syntax.Identifier.GetLocation(),
|
||||||
|
symbol.Name));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. 必须是 partial
|
||||||
|
if (syntax.Modifiers.All(m => m.Kind() != SyntaxKind.PartialKeyword))
|
||||||
{
|
{
|
||||||
context.ReportDiagnostic(Diagnostic.Create(
|
context.ReportDiagnostic(Diagnostic.Create(
|
||||||
PriorityDiagnostic.MustBePartial,
|
PriorityDiagnostic.MustBePartial,
|
||||||
@ -55,7 +65,7 @@ public sealed class PriorityGenerator : MetadataAttributeClassGeneratorBase
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 3. 检查是否已手动实现 IPrioritized
|
// 4. 检查是否已手动实现 IPrioritized
|
||||||
var iPrioritized = compilation.GetTypeByMetadataName(
|
var iPrioritized = compilation.GetTypeByMetadataName(
|
||||||
$"{PathContests.CoreAbstractionsNamespace}.bases.IPrioritized");
|
$"{PathContests.CoreAbstractionsNamespace}.bases.IPrioritized");
|
||||||
|
|
||||||
@ -68,7 +78,7 @@ public sealed class PriorityGenerator : MetadataAttributeClassGeneratorBase
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 4. 验证特性参数
|
// 5. 验证特性参数
|
||||||
if (attr.ConstructorArguments.Length == 0 ||
|
if (attr.ConstructorArguments.Length == 0 ||
|
||||||
attr.ConstructorArguments[0].Value is not int)
|
attr.ConstructorArguments[0].Value is not int)
|
||||||
{
|
{
|
||||||
@ -129,6 +139,15 @@ public sealed class PriorityGenerator : MetadataAttributeClassGeneratorBase
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
protected override string GetHintName(INamedTypeSymbol symbol)
|
protected override string GetHintName(INamedTypeSymbol symbol)
|
||||||
{
|
{
|
||||||
return $"{symbol.Name}.Priority.g.cs";
|
// 使用完整的元数据名称以避免冲突
|
||||||
|
var metadataName = symbol.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat)
|
||||||
|
.Replace("global::", "")
|
||||||
|
.Replace("<", "_")
|
||||||
|
.Replace(">", "_")
|
||||||
|
.Replace(",", "_")
|
||||||
|
.Replace(" ", "")
|
||||||
|
.Replace(".", "_");
|
||||||
|
|
||||||
|
return $"{metadataName}.Priority.g.cs";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -60,4 +60,30 @@ internal static class PriorityDiagnostic
|
|||||||
isEnabledByDefault: true,
|
isEnabledByDefault: true,
|
||||||
description: "Priority 特性必须提供一个有效的整数值。"
|
description: "Priority 特性必须提供一个有效的整数值。"
|
||||||
);
|
);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// GF_Priority_005: Priority 不支持嵌套类
|
||||||
|
/// </summary>
|
||||||
|
public static readonly DiagnosticDescriptor NestedClassNotSupported = new(
|
||||||
|
id: "GF_Priority_005",
|
||||||
|
title: "Priority 不支持嵌套类",
|
||||||
|
messageFormat: "Priority 特性不支持嵌套类 '{0}',请将类移至顶层",
|
||||||
|
category: Category,
|
||||||
|
defaultSeverity: DiagnosticSeverity.Error,
|
||||||
|
isEnabledByDefault: true,
|
||||||
|
description: "Priority 特性仅支持顶层类,不支持嵌套类。请将嵌套类移至命名空间级别。"
|
||||||
|
);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// GF_Priority_Usage_001: 建议使用 GetAllByPriority
|
||||||
|
/// </summary>
|
||||||
|
public static readonly DiagnosticDescriptor SuggestGetAllByPriority = new(
|
||||||
|
id: "GF_Priority_Usage_001",
|
||||||
|
title: "建议使用 GetAllByPriority",
|
||||||
|
messageFormat: "类型 '{0}' 实现了 IPrioritized 接口,建议使用 GetAllByPriority<{0}>() 而非 GetAll<{0}>()",
|
||||||
|
category: "GFramework.Usage",
|
||||||
|
defaultSeverity: DiagnosticSeverity.Info,
|
||||||
|
isEnabledByDefault: true,
|
||||||
|
description: "当获取实现了 IPrioritized 接口的服务时,应使用 GetAllByPriority 方法以确保按优先级排序。"
|
||||||
|
);
|
||||||
}
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user