Analyzing .NET Performance
dotnet/skills
Scans C# and .NET code for about 50 performance anti-patterns and reports prioritized findings with concrete fixes, at a scan depth you choose.
Guides making .NET libraries trimming-safe and Native-AOT compatible: the MSBuild properties, trimming attributes, warning codes and a playbook of safe patterns.
$ npx skills add Aaronontheweb/dotnet-skills --skill aot-trimming -a claude-codeProject install by default; add -g for ~/.claude/skills/.
$ gh skill install Aaronontheweb/dotnet-skills aot-trimming --agent claude-codeProject scope by default; add --scope user for a personal install. Needs GitHub CLI 2.90.0 or later (public preview).
$ git clone --depth 1 https://github.com/Aaronontheweb/dotnet-skills.git skills-src && mkdir -p .claude/skills && cp -r skills-src/skills/aot-trimming .claude/skills/aot-trimming && rm -rf skills-srcUse ~/.claude/skills/ instead of .claude/skills for a personal install. The folder must contain SKILL.md.
Claude Code skills documentation · loads skills from .claude/skills/
Install the "aot-trimming" agent skill from https://github.com/Aaronontheweb/dotnet-skills/tree/master/skills/aot-trimming into .claude/skills/aot-trimming/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "aot-trimming", then confirm the skill loads.Claude Code copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$skill-installer install https://github.com/Aaronontheweb/dotnet-skills/tree/master/skills/aot-trimmingType this inside Codex. $skill-installer <name> installs a curated skill from openai/skills. The installer writes to $CODEX_HOME/skills (default ~/.codex/skills). Restart Codex if the skill does not show up.
$ npx skills add Aaronontheweb/dotnet-skills --skill aot-trimming -a codexProject install goes to .agents/skills/; add -g for ~/.codex/skills/.
$ gh skill install Aaronontheweb/dotnet-skills aot-trimming --agent codexProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/Aaronontheweb/dotnet-skills.git skills-src && mkdir -p .agents/skills && cp -r skills-src/skills/aot-trimming .agents/skills/aot-trimming && rm -rf skills-srcUse ~/.agents/skills/ instead of .agents/skills for a personal install.
Codex skills documentation · loads skills from .agents/skills/
Install the "aot-trimming" agent skill from https://github.com/Aaronontheweb/dotnet-skills/tree/master/skills/aot-trimming into .agents/skills/aot-trimming/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "aot-trimming", then confirm the skill loads.Codex copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ npx skills add Aaronontheweb/dotnet-skills --skill aot-trimming -a cursorProject install goes to .agents/skills/; add -g for ~/.cursor/skills/.
$ gh skill install Aaronontheweb/dotnet-skills aot-trimming --agent cursorProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/Aaronontheweb/dotnet-skills.git skills-src && mkdir -p .cursor/skills && cp -r skills-src/skills/aot-trimming .cursor/skills/aot-trimming && rm -rf skills-srcUse ~/.cursor/skills/ instead of .cursor/skills for a personal install.
Cursor skills documentation · loads skills from .cursor/skills/, .agents/skills/, .claude/skills/, .codex/skills/
Install the "aot-trimming" agent skill from https://github.com/Aaronontheweb/dotnet-skills/tree/master/skills/aot-trimming into .cursor/skills/aot-trimming/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "aot-trimming", then confirm the skill loads.Cursor copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ gemini skills install https://github.com/Aaronontheweb/dotnet-skills.git --path skills/aot-trimming--scope user (default) or --scope workspace; --path is the subfolder of the repo that holds the skill; --consent skips the security confirmation prompt.
$ npx skills add Aaronontheweb/dotnet-skills --skill aot-trimming -a gemini-cliProject install goes to .agents/skills/; add -g for ~/.gemini/skills/.
$ gh skill install Aaronontheweb/dotnet-skills aot-trimming --agent gemini-cliProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/Aaronontheweb/dotnet-skills.git skills-src && mkdir -p .gemini/skills && cp -r skills-src/skills/aot-trimming .gemini/skills/aot-trimming && rm -rf skills-srcUse ~/.gemini/skills/ instead of .gemini/skills for a personal install, then run /skills reload.
Gemini CLI skills documentation · loads skills from .gemini/skills/, .agents/skills/
Install the "aot-trimming" agent skill from https://github.com/Aaronontheweb/dotnet-skills/tree/master/skills/aot-trimming into .gemini/skills/aot-trimming/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "aot-trimming", then confirm the skill loads.Gemini CLI copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ gh skill install Aaronontheweb/dotnet-skills aot-trimmingInstalls for Copilot at project scope by default; add --scope user for a personal install. Preview a skill first with gh skill preview. Needs GitHub CLI 2.90.0 or later (public preview).
$ npx skills add Aaronontheweb/dotnet-skills --skill aot-trimming -a github-copilotProject install goes to .agents/skills/; add -g for ~/.copilot/skills/.
$ git clone --depth 1 https://github.com/Aaronontheweb/dotnet-skills.git skills-src && mkdir -p .github/skills && cp -r skills-src/skills/aot-trimming .github/skills/aot-trimming && rm -rf skills-srcUse ~/.copilot/skills/ instead of .github/skills for a personal install. Commit .github/skills so cloud agent and code review can use it.
GitHub Copilot skills documentation · loads skills from .github/skills/, .claude/skills/, .agents/skills/
Install the "aot-trimming" agent skill from https://github.com/Aaronontheweb/dotnet-skills/tree/master/skills/aot-trimming into .github/skills/aot-trimming/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "aot-trimming", then confirm the skill loads.GitHub Copilot copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ npx skills add Aaronontheweb/dotnet-skills --skill aot-trimming -a opencodeOpenCode documents no install command of its own. Project install goes to .agents/skills/; add -g for ~/.config/opencode/skills/.
$ gh skill install Aaronontheweb/dotnet-skills aot-trimming --agent opencodeProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/Aaronontheweb/dotnet-skills.git skills-src && mkdir -p .opencode/skills && cp -r skills-src/skills/aot-trimming .opencode/skills/aot-trimming && rm -rf skills-srcUse ~/.config/opencode/skills/ instead of .opencode/skills for a personal install.
OpenCode skills documentation · loads skills from .opencode/skills/, .claude/skills/, .agents/skills/
Install the "aot-trimming" agent skill from https://github.com/Aaronontheweb/dotnet-skills/tree/master/skills/aot-trimming into .opencode/skills/aot-trimming/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "aot-trimming", then confirm the skill loads.OpenCode copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
aot-trimmingGuides making .NET libraries trimming-safe and Native-AOT compatible: the MSBuild properties, trimming attributes, warning codes and a playbook of safe patterns.
Trimming runs the linker's reachability analysis and keeps only statically reachable code, warning when it cannot see reflection it needs to trim away; Native AOT makes trimming mandatory and removes the JIT entirely, so Reflection.Emit stops working and Expression.Compile may fall back to interpretation. Both run the same two families of static analysis, trimming warnings from RequiresUnreferencedCode propagation and DynamicallyAccessedMembers dataflow, and AOT and single-file warnings from RequiresDynamicCode and RequiresAssemblyFiles; a library can be trimmable without being AOT-compatible if it relies on Reflection.Emit.
The two problems to solve are unseen reflection, where the linker removes a member loaded by name or reached through a generic type parameter, and dynamic code generation, where the missing JIT breaks runtime code emission; both are solved by making the needed members statically visible to the analyzer or removing the need for reflection. The skill covers the MSBuild properties that enable this analysis, the attributes that express what the compiler cannot see, and a playbook of patterns including source generators, diagnostic suppressors, trimming-safe islands behind feature switches, and temporary warning-approval baselines, with a separate skill for JSON serialization's own AOT scenarios.
2 steps, taken from the first numbered list in SKILL.md.
Read from SKILL.md and the folder at commit 46003af. It shows what the files ask for, not the result of running them.
Pre-approves nothing: there is no allowed-tools line, so your agent's usual permission prompts apply.
From allowed-tools in the SKILL.md frontmatter.
Shell commands in SKILL.md call:
dotnetFrom the folder's file list and the shell code blocks in SKILL.md.
Links to these hosts (documentation or services it may open):
learn.microsoft.comgithub.comFrom URLs in SKILL.md, links to its own repository left out.
Names no API keys, tokens, secrets or passwords.
From names ending in _API_KEY, _TOKEN, _SECRET, _KEY or _PASSWORD in SKILL.md.
.NET Trimming and Native AOT loads about 2.9k tokens when it runs. Until then it costs about 219 tokens; SKILL.md has 1,015 words of instructions outside code blocks.
Estimates: characters ÷ 4, the usual rule of thumb; real counts depend on the model's tokenizer. Scripts and assets cost tokens only if the agent reads them.
The automated check found no risky patterns in SKILL.md.
Automated static check — not a guarantee. Review scripts before installing. It scans the text of SKILL.md for risky patterns (piping downloads into a shell, reading credential files, hidden Unicode, destructive commands); files beside SKILL.md are not scanned.
The full file from Aaronontheweb/dotnet-skills at commit 46003af, republished under its MIT licence (© Aaronontheweb). 1,015 words, ~2,915 tokens.
.claude/skills/aot-trimming/SKILL.md (or your agent's skills folder). This skill also uses 2 other files; get the full folder from GitHub.PublishTrimmed) or Native AOT (PublishAot) support to a library or applicationIL2xxx (trimming) and IL3xxx (AOT/single-file) warningsReflection.Emit with compile-time alternativesPublishTrimmed) runs ILLink reachability analysis: only statically reachable code is kept. Reflection the analyzer cannot see is trimmed away and produces warnings.PublishAot) makes trimming mandatory and removes the JIT. Reflection.Emit is unsupported, constructing unknown generic instantiations at runtime is not guaranteed, Expression.Compile() may fall back to interpretation, and reflection works only over members the linker preserved.IL2xxx — trimming warnings: RequiresUnreferencedCode propagation + DynamicallyAccessedMembers dataflow.IL3xxx — AOT and single-file warnings: RequiresDynamicCode / RequiresAssemblyFiles.A library can be trimmable but not AOT-compatible (it uses Reflection.Emit, which trimming tolerates but AOT does not).
typeof(T) in a generic method.Reflection.Emit is unsupported and constructing unknown generic instantiations is not guaranteed.Both are solved the same way: make the required members statically visible to the analyzer, or remove the need for reflection entirely.
Reflection is a legitimate technique and is completely fine in ordinary JIT applications — you need none of this there. The trimming attributes exist to allow reflection under trimming/AOT, not to forbid it: annotate what reflection needs, and the linker keeps it. Reach for source generators, [UnsafeAccessor], or capability gating only when you want a specific reflective surface removed or contained (for example, a public registration API you do not want to ship with [RequiresUnreferencedCode]).
For a library, declare compatibility so the analyzers surface warnings and consumers know:
<PropertyGroup>
<!-- Trim-compatible: enables trim warnings. -->
<IsTrimmable>true</IsTrimmable>
<!-- AOT-compatible: implies IsTrimmable + EnableTrimAnalyzer + EnableSingleFileAnalyzer + EnableAotAnalyzer. -->
<IsAotCompatible Condition="$([MSBuild]::IsTargetFrameworkCompatible('$(TargetFramework)', 'net8.0'))">true</IsAotCompatible>
</PropertyGroup>For an application, publish trimmed or AOT:
<PropertyGroup>
<PublishTrimmed>true</PublishTrimmed>
<!-- or -->
<PublishAot>true</PublishAot>
</PropertyGroup>Verify by actually publishing — a trimmed/AOT app must produce zero warnings:
dotnet publish -c Release -r <rid> -p:PublishTrimmed=true
dotnet publish -c Release -r <rid> -p:PublishAot=trueA test app that roots the library is the standard way to validate a library:
<PropertyGroup>
<PublishTrimmed>true</PublishTrimmed>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\MyLibrary\MyLibrary.csproj" />
<TrimmerRootAssembly Include="MyLibrary" />
</ItemGroup>See aot-trimming-playbook-reference.md for the full property table, feature switches, and the warning-approval baseline pattern.
These live in System.Diagnostics.CodeAnalysis. See trimming-attributes-reference.md for the full catalog with exact signatures, warning codes, and rules.
[RequiresUnreferencedCode(message)] — marks code that needs members the linker cannot see. Suppresses warnings inside; emits IL2026 at every call site.[RequiresDynamicCode(message)] — marks code that needs a JIT (emitting, dynamic). Emits IL3050 at call sites.[DynamicallyAccessedMembers(memberTypes)] — declares which members of a Type/string must be preserved. Flows backward from the reflection site to the Type source. Scope it (and the other attributes) to a single accessor by placing the attribute directly on the accessor, or with the [method:], [field:], or [return:] targets.[UnconditionalSuppressMessage(category, checkId, Justification = "...")] — last-resort, IL-persisted suppression; must carry a justification and a real invariant.[DynamicDependency("Member", typeof(T))] — keeps named members but does not silence warnings.Choose the narrowest DynamicallyAccessedMemberTypes for the reflection you actually perform:
| Reflection | Member type |
|---|---|
Activator.CreateInstance<T>(), new T(), Activator.CreateInstance(type) | PublicParameterlessConstructor |
DI activation (ActivatorUtilities.CreateFactory<T>) | PublicConstructors |
| Explicit non-public activation | NonPublicConstructors |
type.GetInterfaces() | Interfaces |
type.GetMethods() | PublicMethods (add NonPublicMethods when needed) |
type.GetProperties() | PublicProperties |
type.GetFields() | PublicFields |
type.GetNestedTypes() | PublicNestedTypes |
First decide whether the code must be trimming/AOT safe at all. A plain JIT app or library needs none of this — reflection is fine there. Only when you or a consumer publish trimmed/AOT does reflection need to become visible to the linker.
The goal is never "remove all reflection"; it is make the reflection statically visible so the linker keeps what it needs. Choose per call site:
Keep the reflection and annotate it — the common case. Put [DynamicallyAccessedMembers(...)] on the Type/string source and propagate it through the chain; mark entry points [RequiresUnreferencedCode]/[RequiresDynamicCode]. The attributes exist precisely to let reflection work under trimming. See trimming-attributes-reference.md.
Replace the reflection with a compile-time alternative — when you want a reflective surface gone (for example, a public registration API you do not want to ship with [RequiresUnreferencedCode]). A source generator emits direct type references (marker attribute → generated registration); [UnsafeAccessor] replaces FieldInfo/MethodInfo/ConstructorInfo. See aot-trimming-playbook-reference.md and aot-trimming-playbook-reference.md.
Isolate a whole dynamic path — when a path is inherently dynamic (emitting proxies, loading plugins by name), move it behind a boundary and provide a trimming-safe alternative selected by RuntimeFeature.IsDynamicCodeSupported. See aot-trimming-playbook-reference.md.
Suppress only as a last resort — a single [UnconditionalSuppressMessage(...)] at an invariant-proven leaf, with a Justification. Never #pragma (not persisted in IL).
See aot-trimming-playbook-reference.md for the full playbook, including object-overload → generic-overload redirects, generated-interception suppressors, intentional scanning boundaries, migration analyzers, strict-mode precedence, and temporary warning-approval baselines.
RequiresUnreferencedCode, RequiresDynamicCode, DynamicallyAccessedMembers (full DynamicallyAccessedMemberTypes list), UnconditionalSuppressMessage, DynamicDependency — each with intent, exact signature, and rules, plus the IL2xxx/IL3xxx warning-code table.[OverloadResolutionPriority] and opt-in analyzers, migration analyzers, warning-approval baselines, known gotchas, and the full generation checklist.IsTrimmable/IsAotCompatible on libraries; PublishTrimmed/PublishAot on apps; zero warnings on publish.DynamicallyAccessedMembers/RequiresUnreferencedCode/RequiresDynamicCode); replace it with source generators, generic (typeof(T)) APIs, or [UnsafeAccessor] only where you want the reflective surface gone.DynamicallyAccessedMembers on every Type/string source; propagate through the chain; [RequiresUnreferencedCode]/[RequiresDynamicCode] on unsafe entry points.RuntimeFeature.IsDynamicCodeSupported, and separate trimming paths with #if or annotations.[UnconditionalSuppressMessage] + justification at invariant-proven leaves; never #pragma/SuppressMessage.© Aaronontheweb, MIT. Rendered from Markdown: HTML in the file is shown as text, images as links, and headings moved down two levels. Raw file
SKILL.md and 2 other files in skills/aot-trimming of Aaronontheweb/dotnet-skills.
Open the folder on GitHubat commit 46003af
.NET Trimming and Native AOT next to the 5 skills that share the most tags, products or categories with it. Stars are the repository's; “used in” counts other GitHub owners with a copy.
| Skill | Stars | Used in | Tokens | Auto-check | Licence | Repo updated |
|---|---|---|---|---|---|---|
| .NET Trimming and Native AOT this skillAaronontheweb/dotnet-skills | 1.2k | — | ~2.9k | Automated safety check: Pass | MIT | |
| Analyzing .NET Performancedotnet/skills | 5.6k | 3 repos | ~3.1k | Automated safety check: Pass | MIT | |
| MAUI PR Performance Analysisdotnet/maui | 23k | — | ~2.4k | Automated safety check: Pass | MIT | |
| Dotnet Debuggingnovotnyllc/dotnet-artisan | 232 | — | ~2.1k | Automated safety check: Pass | MIT | |
| Build Dotnet Agentnewrelic/newrelic-dotnet-agent | 117 | — | ~871 | Automated safety check: Pass | Apache-2.0 | |
| Asynkron Profilermanagedcode/dotnet-skills | 486 | — | ~1.7k | Automated safety check: Pass | MIT |
dotnet/skills
Scans C# and .NET code for about 50 performance anti-patterns and reports prioritized findings with concrete fixes, at a scan depth you choose.
dotnet/maui
Interprets pinned managed benchmark evidence for a dotnet/maui pull request and writes a narrative for the performance review workflow, without running or publishing anything.
novotnyllc/dotnet-artisan
Debugs Windows and Linux/macOS applications (native, .NET/CLR, mixed-mode) with WinDbg MCP (crash dumps, !analyze, !syncblk, !dlk, !runaway, !dumpheap, !gcroot, BSOD), dotnet-dump, lldb with SOS…
newrelic/newrelic-dotnet-agent
Build the New Relic .NET agent locally and validate that code changes compile.
managedcode/dotnet-skills
Use the open-source free Asynkron.Profiler dotnet tool for CLI-first CPU, allocation, exception, contention, and heap profiling of .NET commands or existing trace artifacts.
newrelic/newrelic-dotnet-agent
Parse and diagnose New Relic .NET agent logs (newrelicagent.log) and profiler logs (NewRelic.Profiler.<pid.log) from a support ticket.
Aaronontheweb/dotnet-skills
Write and simplify C property-based, model-based, and executable specification tests with CsCheck.
Aaronontheweb/dotnet-skills
Shows how to build entity actors with Akka.Hosting so the same code runs in local unit tests and in a sharded cluster in production.
Aaronontheweb/dotnet-skills
Publish .NET services as container images with the built-in SDK tooling (dotnet publish /t:PublishContainer, Microsoft.NET.Build.Containers) - no Dockerfile required.
Aaronontheweb/dotnet-skills
Guidance for Akka.NET actor systems covering EventStream versus DistributedPubSub, supervision, Props versus DependencyResolver, work distribution and testable cluster code.
Aaronontheweb/dotnet-skills
Sets up Akka.Management and Cluster.Bootstrap so Akka.NET clusters form through service discovery on Kubernetes, Azure or config instead of static seed nodes.
Aaronontheweb/dotnet-skills
Shows how to test Akka.NET actors with Akka.Hosting.TestKit: swapping services for fakes, using TestProbes, and checking persistence, plus when the older TestKit still fits.
Works with
Categories
Guides making .NET libraries trimming-safe and Native-AOT compatible: the MSBuild properties, trimming attributes, warning codes and a playbook of safe patterns. Compile may fall back to interpretation.Emit.
.NET Trimming and Native AOT fits situations like: adding trimming or Native AOT support to a .NET library or application; resolving trimming or AOT warning codes; making reflection-heavy code trimming-safe; reviewing a PR for trimming and AOT compatibility.
Run `npx skills add Aaronontheweb/dotnet-skills --skill aot-trimming -a claude-code`. Or copy the skill folder (skills/aot-trimming in Aaronontheweb/dotnet-skills) into .claude/skills/aot-trimming in your project. Claude Code loads it when a task matches its description.
Run `npx skills add Aaronontheweb/dotnet-skills --skill aot-trimming -a codex`. Or copy the skill folder (skills/aot-trimming in Aaronontheweb/dotnet-skills) into .agents/skills/aot-trimming in your project. Codex loads it when a task matches its description.
Cursor, Gemini CLI, GitHub Copilot and OpenCode also load SKILL.md folders. With the skills CLI, run `npx skills add Aaronontheweb/dotnet-skills --skill aot-trimming -a cursor` (or -a gemini-cli, github-copilot or opencode for the others). To copy it by hand, put the folder in .cursor/skills/aot-trimming, .gemini/skills/aot-trimming, .github/skills/aot-trimming and .opencode/skills/aot-trimming in your project.
Going by SKILL.md and its folder, .NET Trimming and Native AOT needs the command-line tools its instructions call (dotnet). Our summary lists: A .NET project targeting PublishTrimmed or PublishAot.
SKILL.md names 2 domains. As links in the text: learn.microsoft.com and github.com. This is read from the text; nothing was executed.
Our automated static check of SKILL.md found no risky patterns, such as piping downloads into a shell, reading credential files or hidden Unicode. It is not a guarantee. Review the folder before installing.
.NET Trimming and Native AOT is published under the MIT licence (the repository's licence). It allows redistribution, so the full SKILL.md is shown on this page.
About 2.9k tokens (SKILL.md is roughly 12k characters). Agents keep only the skill's name and description in context until a task matches; then they load SKILL.md in full.
Skills that share tags, products or a category with .NET Trimming and Native AOT: Analyzing .NET Performance (dotnet/skills, 5.6k stars), MAUI PR Performance Analysis (dotnet/maui, 23k stars), Dotnet Debugging (novotnyllc/dotnet-artisan, 232 stars) and Build Dotnet Agent (newrelic/newrelic-dotnet-agent, 117 stars). The comparison table on this page puts their stars, adoption, token cost, safety result and licence side by side.
Aaronontheweb (a GitHub user) maintains it in Aaronontheweb/dotnet-skills, which has 1,206 GitHub stars. The repository holds 35 skills in this directory. The repository was last updated on October 10, 2026.
Source: Aaronontheweb/dotnet-skills on GitHub. Facts on this page come from the repository at the commit we read; the author's words are quoted as theirs.