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>
|
||||
/// 获取指定类型的所有实例,并按优先级排序
|
||||
/// 实现 IPrioritized 接口的服务将按值越小优先级越高)
|
||||
/// 实现 IPrioritized 接口的服务将按优先级排序(数值越小优先级越高)
|
||||
/// 未实现 IPrioritized 的服务将使用默认优先级 0
|
||||
/// </summary>
|
||||
/// <param name="type">期望获取的实例类型</param>
|
||||
@ -632,17 +632,16 @@ public class MicrosoftDiContainer(IServiceCollection? serviceCollection = null)
|
||||
if (services.Count <= 1)
|
||||
return services;
|
||||
|
||||
var list = services.ToList();
|
||||
|
||||
// 稳定排序:相同优先级保持注册顺序
|
||||
list.Sort((a, b) =>
|
||||
{
|
||||
var priorityA = a is IPrioritized pa ? pa.Priority : 0;
|
||||
var priorityB = b is IPrioritized pb ? pb.Priority : 0;
|
||||
return priorityA.CompareTo(priorityB); // 升序
|
||||
});
|
||||
|
||||
return list;
|
||||
// 使用 OrderBy 确保稳定排序(相同优先级保持原有顺序)
|
||||
return services
|
||||
.Select((service, index) => new { Service = service, Index = index })
|
||||
.OrderBy(x =>
|
||||
{
|
||||
var priority = x.Service is IPrioritized p ? p.Priority : 0;
|
||||
return (priority, x.Index); // 先按优先级,再按索引
|
||||
})
|
||||
.Select(x => x.Service)
|
||||
.ToList();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
@ -212,55 +212,4 @@ public class PriorityGeneratorSnapshotTests
|
||||
"PriorityGenerator",
|
||||
"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_003 | 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
|
||||
@ -1,4 +1,5 @@
|
||||
using System.Collections.Immutable;
|
||||
using GFramework.SourceGenerators.diagnostics;
|
||||
using Microsoft.CodeAnalysis;
|
||||
using Microsoft.CodeAnalysis.Diagnostics;
|
||||
using Microsoft.CodeAnalysis.Operations;
|
||||
@ -11,27 +12,11 @@ namespace GFramework.SourceGenerators.analyzers;
|
||||
[DiagnosticAnalyzer(LanguageNames.CSharp)]
|
||||
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>
|
||||
public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics => ImmutableArray.Create(Rule);
|
||||
public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics =>
|
||||
ImmutableArray.Create(PriorityDiagnostic.SuggestGetAllByPriority);
|
||||
|
||||
/// <summary>
|
||||
/// 初始化分析器
|
||||
@ -112,7 +97,7 @@ public sealed class PriorityUsageAnalyzer : DiagnosticAnalyzer
|
||||
|
||||
// 报告诊断
|
||||
var diagnostic = Diagnostic.Create(
|
||||
Rule,
|
||||
PriorityDiagnostic.SuggestGetAllByPriority,
|
||||
invocation.Syntax.GetLocation(),
|
||||
typeArgument.ToDisplayString());
|
||||
|
||||
|
||||
@ -45,8 +45,18 @@ public sealed class PriorityGenerator : MetadataAttributeClassGeneratorBase
|
||||
return false;
|
||||
}
|
||||
|
||||
// 2. 必须是 partial
|
||||
if (!syntax.Modifiers.Any(SyntaxKind.PartialKeyword))
|
||||
// 2. 不支持嵌套类
|
||||
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(
|
||||
PriorityDiagnostic.MustBePartial,
|
||||
@ -55,7 +65,7 @@ public sealed class PriorityGenerator : MetadataAttributeClassGeneratorBase
|
||||
return false;
|
||||
}
|
||||
|
||||
// 3. 检查是否已手动实现 IPrioritized
|
||||
// 4. 检查是否已手动实现 IPrioritized
|
||||
var iPrioritized = compilation.GetTypeByMetadataName(
|
||||
$"{PathContests.CoreAbstractionsNamespace}.bases.IPrioritized");
|
||||
|
||||
@ -68,7 +78,7 @@ public sealed class PriorityGenerator : MetadataAttributeClassGeneratorBase
|
||||
return false;
|
||||
}
|
||||
|
||||
// 4. 验证特性参数
|
||||
// 5. 验证特性参数
|
||||
if (attr.ConstructorArguments.Length == 0 ||
|
||||
attr.ConstructorArguments[0].Value is not int)
|
||||
{
|
||||
@ -129,6 +139,15 @@ public sealed class PriorityGenerator : MetadataAttributeClassGeneratorBase
|
||||
/// </summary>
|
||||
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,
|
||||
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