detect-static-dependencies

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Detect Static Dependencies

检测静态依赖项

Scan a C# codebase for calls to hard-to-test static APIs and produce a ranked report showing which statics appear most frequently, which files are most affected, and which abstractions already exist in the .NET ecosystem to replace them.
扫描C#代码库中调用难以测试的静态API的情况,生成排序报告,显示哪些静态成员出现频率最高、哪些文件受影响最严重,以及.NET生态系统中已有的可替代抽象层。

When to Use

适用场景

  • Auditing a project's testability before adding unit tests
  • Understanding the scope of static coupling in a legacy codebase
  • Prioritizing which statics to wrap first (highest-frequency wins)
  • Creating a migration plan for incremental testability improvements
  • 在添加单元测试前审计项目的可测试性
  • 了解遗留代码库中静态耦合的范围
  • 确定优先包装哪些静态成员(调用频率最高的优先)
  • 创建增量提升可测试性的迁移计划

When Not to Use

不适用场景

  • The user wants wrappers generated (hand off to
    generate-testability-wrappers
    )
  • The user wants mechanical migration done (hand off to
    migrate-static-to-wrapper
    )
  • The statics are already behind interfaces or
    TimeProvider
  • The code is not C# / .NET
  • 用户需要生成包装器(请转交至
    generate-testability-wrappers
    )
  • 用户需要自动完成代码迁移(请转交至
    migrate-static-to-wrapper
    )
  • 静态成员已被接口或
    TimeProvider
    封装
  • 代码非C#/.NET技术栈

Inputs

输入参数

InputRequiredDescription
Target pathYesA file, directory, project (.csproj), or solution (.sln) to scan
Exclusion patternsNoGlob patterns to skip (e.g.,
**/obj/**
,
**/Migrations/**
)
Category filterNoLimit to specific categories:
time
,
filesystem
,
environment
,
network
,
console
,
process
输入项是否必填描述
目标路径是要扫描的文件、目录、项目(.csproj)或解决方案(.sln)
排除模式否要跳过的Glob模式(例如
**/obj/**
、
**/Migrations/**
)
类别筛选否限制为特定类别:
time
、
filesystem
、
environment
、
network
、
console
、
process

Workflow

工作流程

Step 1: Determine scan scope

步骤1:确定扫描范围

Resolve the target to a set of
.cs
files:
  • If a
    .cs
    file, scan that single file.
  • If a directory, scan all
    .cs
    files recursively (excluding
    obj/
    ,
    bin/
    ).
  • If a
    .csproj
    , find its directory and scan
    .cs
    files within.
  • If a
    .sln
    , parse it, find all project directories, and scan
    .cs
    files across all projects.
Always exclude
obj/
,
bin/
, and any user-specified exclusion patterns.
将目标解析为一组
.cs
文件:
  • 如果是
    .cs
    文件,仅扫描该单个文件。
  • 如果是目录,递归扫描所有
    .cs
    文件(排除
    obj/
    、
    bin/
    )。
  • 如果是
    .csproj
    ,找到其所在目录并扫描其中的
    .cs
    文件。
  • 如果是
    .sln
    ,解析它,找到所有项目目录,扫描所有项目中的
    .cs
    文件。
始终排除
obj/
、
bin/
以及用户指定的任何排除模式。

Step 2: Search for static dependency patterns

步骤2:搜索静态依赖模式

Scan each file for calls matching these categories:
CategoryPatterns to search forRecommended replacement
Time
DateTime.Now
,
DateTime.UtcNow
,
DateTime.Today
,
DateTimeOffset.Now
,
DateTimeOffset.UtcNow
,
Task.Delay(
,
new CancellationTokenSource(TimeSpan
TimeProvider
(.NET 8+)
File System
File.ReadAllText(
,
File.WriteAllText(
,
File.Exists(
,
File.Delete(
,
File.Copy(
,
File.Move(
,
Directory.Exists(
,
Directory.CreateDirectory(
,
Directory.GetFiles(
,
Directory.Delete(
,
Path.Combine(
,
Path.GetTempPath(
IFileSystem
(System.IO.Abstractions NuGet)
Environment
Environment.GetEnvironmentVariable(
,
Environment.SetEnvironmentVariable(
,
Environment.MachineName
,
Environment.UserName
,
Environment.CurrentDirectory
,
Environment.Exit(
Custom
IEnvironmentProvider
Network
new HttpClient(
,
HttpClient.GetAsync(
,
HttpClient.PostAsync(
,
HttpClient.SendAsync(
IHttpClientFactory
(built-in)
Console
Console.WriteLine(
,
Console.ReadLine(
,
Console.Write(
,
Console.ReadKey(
IConsole
wrapper or
ILogger
Process
Process.Start(
,
Process.GetCurrentProcess(
,
Process.GetProcessesByName(
Custom
IProcessRunner
扫描每个文件,查找匹配以下类别的调用:
类别要搜索的模式推荐替代方案
时间
DateTime.Now
、
DateTime.UtcNow
、
DateTime.Today
、
DateTimeOffset.Now
、
DateTimeOffset.UtcNow
、
Task.Delay(
、
new CancellationTokenSource(TimeSpan
TimeProvider
(.NET 8+)
文件系统
File.ReadAllText(
、
File.WriteAllText(
、
File.Exists(
、
File.Delete(
、
File.Copy(
、
File.Move(
、
Directory.Exists(
、
Directory.CreateDirectory(
、
Directory.GetFiles(
、
Directory.Delete(
、
Path.Combine(
、
Path.GetTempPath(
IFileSystem
(System.IO.Abstractions NuGet包)
环境
Environment.GetEnvironmentVariable(
、
Environment.SetEnvironmentVariable(
、
Environment.MachineName
、
Environment.UserName
、
Environment.CurrentDirectory
、
Environment.Exit(
自定义
IEnvironmentProvider
网络
new HttpClient(
、
HttpClient.GetAsync(
、
HttpClient.PostAsync(
、
HttpClient.SendAsync(
IHttpClientFactory
(内置)
控制台
Console.WriteLine(
、
Console.ReadLine(
、
Console.Write(
、
Console.ReadKey(
自定义
IConsole
包装器或
ILogger
进程
Process.Start(
、
Process.GetCurrentProcess(
、
Process.GetProcessesByName(
自定义
IProcessRunner

Step 3: Aggregate and rank results

步骤3:聚合并排序结果

Count each static call pattern across the entire scan scope. Produce a summary with:
  1. Category summary — total call sites per category (time, filesystem, env, etc.)
  2. Top patterns — the 10 most frequent individual patterns ranked by count
  3. Most affected files — files with the highest number of static dependencies
  4. Existing abstractions available — for each category, note the recommended .NET abstraction:
    • Time →
      TimeProvider
      (built-in since .NET 8)
    • File system →
      System.IO.Abstractions
      (NuGet package)
    • HTTP →
      IHttpClientFactory
      (built-in)
    • Environment → custom
      IEnvironmentProvider
    • Console → custom
      IConsole
      or
      ILogger
    • Process → custom
      IProcessRunner
统计整个扫描范围内每个静态调用模式的出现次数。生成包含以下内容的摘要:
  1. 类别摘要——每个类别(时间、文件系统、环境等)的总调用点数量
  2. 高频模式——按调用次数排序的10个最频繁的单个模式
  3. 受影响最严重的文件——包含最多静态依赖项的文件
  4. 可用的现有抽象层——针对每个类别,标注推荐的.NET抽象层:
    • 时间 →
      TimeProvider
      (.NET 8起内置)
    • 文件系统 →
      System.IO.Abstractions
      (NuGet包)
    • HTTP →
      IHttpClientFactory
      (内置)
    • 环境 → 自定义
      IEnvironmentProvider
    • 控制台 → 自定义
      IConsole
      或
      ILogger
    • 进程 → 自定义
      IProcessRunner

Step 4: Present the report

步骤4:生成报告

Format the output as a structured report:
undefined
将输出格式化为结构化报告:
undefined

Static Dependency Report

静态依赖报告

Scope: <project/solution name> Files scanned: <count> Total static call sites: <count>
范围: <项目/解决方案名称> 扫描文件数: <数量> 静态调用点总数: <数量>

Category Summary

类别摘要

CategoryCall SitesRecommended Abstraction
Time42TimeProvider (.NET 8+)
File System31System.IO.Abstractions
Environment12IEnvironmentProvider
.........
类别调用点数量推荐抽象层
时间42TimeProvider (.NET 8+)
文件系统31System.IO.Abstractions
环境12IEnvironmentProvider
.........

Top 10 Patterns

十大高频模式

#PatternCountFiles
1DateTime.UtcNow2814
2File.ReadAllText189
...
#模式次数涉及文件数
1DateTime.UtcNow2814
2File.ReadAllText189
...

Most Affected Files

受影响最严重的文件

FileStatic CallsCategories
Services/OrderProcessor.cs12Time, FileSystem
...
文件路径静态调用次数涉及类别
Services/OrderProcessor.cs12时间, 文件系统
...

Migration Priority

迁移优先级

  1. Time (42 sites) — Use
    TimeProvider
    , zero NuGet dependencies on .NET 8+
  2. File System (31 sites) — Use
    System.IO.Abstractions
    NuGet package
  3. ...
undefined
  1. 时间(42个调用点)—— 使用
    TimeProvider
    ,.NET 8+无需依赖NuGet包
  2. 文件系统(31个调用点)—— 使用
    System.IO.Abstractions
    NuGet包
  3. ...
undefined

Step 5: Suggest next steps

步骤5:建议后续操作

Based on the report, recommend:
  • Which category to tackle first (fewest dependencies, best built-in support)
  • Whether to use
    generate-testability-wrappers
    for custom wrapper generation
  • Whether to use
    migrate-static-to-wrapper
    for mechanical bulk migration
基于报告内容,推荐:
  • 优先处理哪个类别(依赖最少、内置支持最好)
  • 是否使用
    generate-testability-wrappers
    生成自定义包装器
  • 是否使用
    migrate-static-to-wrapper
    进行批量自动迁移

Validation

验证项

  • All
    .cs
    files in scope were scanned (check count)
  • Report includes category totals, top patterns, and affected files
  • Each detected pattern has a recommended replacement listed
  • obj/
    and
    bin/
    directories were excluded
  • Migration priority is ordered by impact (count × ease of replacement)
  • 已扫描范围内所有
    .cs
    文件(检查数量)
  • 报告包含类别总数、高频模式和受影响文件
  • 每个检测到的模式都标注了推荐替代方案
  • 已排除
    obj/
    和
    bin/
    目录
  • 迁移优先级按影响程度排序(调用次数 × 替换难度)

Common Pitfalls

常见陷阱

PitfallSolution
Scanning
obj/
or generated code
Always exclude
obj/
,
bin/
, and
*.Designer.cs
Counting wrapped calls as staticsCheck if the call is behind an interface or injected service before counting
Missing statics inside lambdas/LINQSearch covers all code within
.cs
files, including lambdas
Recommending
TimeProvider
on < .NET 8
Check
TargetFramework
in
.csproj
— if < net8.0, recommend
NodaTime.IClock
or custom
ISystemClock
Ignoring test projectsOnly scan production code — exclude
*.Tests.csproj
projects from the scan
陷阱解决方案
扫描
obj/
或生成的代码
始终排除
obj/
、
bin/
和
*.Designer.cs
将已包装的调用统计为静态依赖在统计前检查调用是否已被接口或注入的服务封装
遗漏Lambda/LINQ中的静态成员扫描覆盖
.cs
文件内所有代码,包括Lambda表达式
在低于.NET 8的版本中推荐
TimeProvider
检查
.csproj
中的
TargetFramework
——如果低于net8.0,推荐
NodaTime.IClock
或自定义
ISystemClock
忽略测试项目仅扫描生产代码——从扫描中排除
*.Tests.csproj
项目