利用 C# 特性 將元數據嵌入程序集,配合反射實現零侵入式代碼統計分析:方法數統計、開發者貢獻度、Bug 標記追蹤、IL 代碼查看。本文提供完整架構設計與核心代碼。
獨立類庫 CodeAnalytics.Attributes,供業務項目引用:
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = false)]
public sealed class CreatorAttribute : Attribute {
public string Creator { get; }
public string CreateDate { get; }
public CreatorAttribute(string creator, string createDate) {
Creator = creator;
CreateDate = createDate;
}
}
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = true)]
public sealed class ReviewerAttribute : Attribute {
public string Reviewer { get; }
public string ReviewDate { get; }
public ReviewerAttribute(string reviewer, string reviewDate) {
Reviewer = reviewer;
ReviewDate = reviewDate;
}
}
[AttributeUsage(AttributeTargets.Method, AllowMultiple = false)]
public sealed class BugAttribute : Attribute {
public bool HasBug { get; }
public string BugDate { get; }
public BugAttribute(bool hasBug, string bugDate) {
HasBug = hasBug;
BugDate = bugDate;
}
}
[Creator("join", "2024-01-15")]
public class OrderService {
[Creator("join", "2024-01-15")]
[Reviewer("alice", "2024-01-16")]
[Bug(false, "")]
public void CreateOrder(OrderDto dto) { /* ... */ }
[Creator("bob", "2024-01-18")]
[Reviewer("alice", "2024-01-19")]
[Reviewer("charlie", "2024-01-20")]
[Bug(true, "2024-01-21")] // 曾有 Bug
public void CancelOrder(int orderId) { /* ... */ }
}
public sealed class AssemblyAnalyzer {
public AnalysisReport Analyze(string assemblyPath) {
var asm = Assembly.LoadFrom(assemblyPath);
var report = new AnalysisReport { AssemblyName = asm.GetName().Name };
foreach (var type in asm.GetTypes()) {
var typeCreator = type.GetCustomAttribute();
foreach (var method in type.GetMethods(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static)) {
if (method.IsSpecialName) continue; // 跳過屬性 get/set、構造函數等
var creator = method.GetCustomAttribute() ?? typeCreator;
var reviewers = method.GetCustomAttributes().ToList();
var bug = method.GetCustomAttribute();
report.Methods.Add(new MethodStat {
FullName = $"{type.FullName}.{method.Name}",
Creator = creator?.Creator ?? "Unknown",
CreateDate = creator?.CreateDate,
Reviewers = reviewers.Select(r => r.Reviewer).ToList(),
HasBug = bug?.HasBug ?? false,
BugDate = bug?.BugDate,
ILCode = GetILCode(method) // 輔助功能
});
}
}
return report;
}
private string GetILCode(MethodInfo method) {
var body = method.GetMethodBody();
if (body == null) return "N/A (Abstract/Extern)";
var il = body.GetILAsByteArray();
return string.Join(" ", il.Select(b => b.ToString("X2")));
}
}
public class AnalysisReport {
public string AssemblyName { get; set; }
public List Methods { get; } = new List();
public int TotalMethods => Methods.Count;
public int BugMethods => Methods.Count(m => m.HasBug);
public Dictionary CreatorStats => Methods
.GroupBy(m => m.Creator)
.ToDictionary(g => g.Key, g => g.Count());
public Dictionary ReviewerStats => Methods
.SelectMany(m => m.Reviewers)
.GroupBy(r => r)
.ToDictionary(g => g.Key, g => g.Count());
}
public class MethodStat {
public string FullName { get; set; }
public string Creator { get; set; }
public string CreateDate { get; set; }
public List Reviewers { get; set; } = new List();
public bool HasBug { get; set; }
public string BugDate { get; set; }
public string ILCode { get; set; }
}
| 方法 | 創建者 | 創建日期 | 審核者 | 有 Bug | Bug 日期 | IL 字節碼 |
|---|---|---|---|---|---|---|
| OrderService.CreateOrder | join | 2024-01-15 | alice | 否 | - | 02 28 0A ... |
| OrderService.CancelOrder | bob | 2024-01-18 | alice, charlie | 是 | 2024-01-21 | 16 72 01 00 00 0A ... |
Attribute + 反射 是構建輕量級代碼治理工具的黃金組合。幾十行核心代碼,即可賦能團隊掌握代碼所有權、審核流轉、缺陷分布。不要讓元數據沉睡在 DLL 裡——讓它們為工程效能發聲。