@cratis/pi 7.101.0 → 7.103.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (168) hide show
  1. package/package/corpus/harnesses/copilot/instructions/github-actions.instructions.md +17 -0
  2. package/package/corpus/harnesses/copilot/instructions/profiles.instructions.md +10 -1
  3. package/package/corpus/harnesses/copilot/instructions/pull-requests.instructions.md +2 -0
  4. package/package/corpus/harnesses/copilot/instructions/secrets.instructions.md +4 -0
  5. package/package/corpus/hooks/README.md +11 -1
  6. package/package/corpus/hooks/scripts/cratis-check-pr.mjs +12 -1
  7. package/package/corpus/hooks/scripts/cratis-guard-secrets.sh +79 -23
  8. package/package/corpus/hooks/scripts/cratis-guard-writes.sh +145 -40
  9. package/package/corpus/rules/github-actions.md +17 -0
  10. package/package/corpus/rules/profiles.md +10 -1
  11. package/package/corpus/rules/pull-requests.md +2 -0
  12. package/package/corpus/rules/secrets.md +4 -0
  13. package/package/corpus/skills/analyzing-dotnet-performance/LICENSE +23 -0
  14. package/package/corpus/skills/analyzing-dotnet-performance/NOTICE +36 -0
  15. package/package/corpus/skills/analyzing-dotnet-performance/SKILL.md +214 -0
  16. package/package/corpus/skills/analyzing-dotnet-performance/references/async-patterns.md +122 -0
  17. package/package/corpus/skills/analyzing-dotnet-performance/references/collections-and-linq.md +220 -0
  18. package/package/corpus/skills/analyzing-dotnet-performance/references/critical-patterns.md +291 -0
  19. package/package/corpus/skills/analyzing-dotnet-performance/references/io-and-serialization.md +135 -0
  20. package/package/corpus/skills/analyzing-dotnet-performance/references/memory-and-strings.md +219 -0
  21. package/package/corpus/skills/analyzing-dotnet-performance/references/regex-patterns.md +95 -0
  22. package/package/corpus/skills/analyzing-dotnet-performance/references/structural-patterns.md +38 -0
  23. package/package/corpus/skills/binlog-generation/LICENSE +23 -0
  24. package/package/corpus/skills/binlog-generation/NOTICE +22 -0
  25. package/package/corpus/skills/binlog-generation/SKILL.md +129 -0
  26. package/package/corpus/skills/check-bin-obj-clash/LICENSE +23 -0
  27. package/package/corpus/skills/check-bin-obj-clash/NOTICE +27 -0
  28. package/package/corpus/skills/check-bin-obj-clash/SKILL.md +127 -0
  29. package/package/corpus/skills/check-bin-obj-clash/references/common-causes-and-fixes.md +206 -0
  30. package/package/corpus/skills/coverage-analysis/LICENSE +23 -0
  31. package/package/corpus/skills/coverage-analysis/NOTICE +32 -0
  32. package/package/corpus/skills/coverage-analysis/SKILL.md +198 -0
  33. package/package/corpus/skills/coverage-analysis/references/guidelines.md +68 -0
  34. package/package/corpus/skills/coverage-analysis/references/output-format.md +91 -0
  35. package/package/corpus/skills/coverage-analysis/references/report-generation.md +58 -0
  36. package/package/corpus/skills/coverage-analysis/references/setup-discovery.md +307 -0
  37. package/package/corpus/skills/coverage-analysis/scripts/Compute-CrapScores.ps1 +80 -0
  38. package/package/corpus/skills/coverage-analysis/scripts/CoverageData.ps1 +272 -0
  39. package/package/corpus/skills/coverage-analysis/scripts/Extract-MethodCoverage.ps1 +65 -0
  40. package/package/corpus/skills/cratis-ci-pipeline-performance/LICENSE +2 -0
  41. package/package/corpus/skills/cratis-ci-pipeline-performance/SKILL.md +201 -0
  42. package/package/corpus/skills/cratis-ci-pipeline-performance/references/build-once-and-shard.md +158 -0
  43. package/package/corpus/skills/cratis-ci-pipeline-performance/references/caching.md +86 -0
  44. package/package/corpus/skills/cratis-ci-pipeline-performance/references/flaky-and-hung-jobs.md +70 -0
  45. package/package/corpus/skills/cratis-ci-pipeline-performance/references/publish-pipelines.md +67 -0
  46. package/package/corpus/skills/cratis-ci-pipeline-performance/references/required-checks-and-path-filters.md +117 -0
  47. package/package/corpus/skills/cratis-ci-pipeline-performance/references/runner-sizing-and-images.md +65 -0
  48. package/package/corpus/skills/cratis-ci-pipeline-performance/references/security-and-pinning.md +73 -0
  49. package/package/corpus/skills/cratis-ci-pipeline-performance/references/sources.md +33 -0
  50. package/package/corpus/skills/cratis-ci-pipeline-performance/references/test-setup-cost.md +82 -0
  51. package/package/corpus/skills/cratis-ci-pipeline-performance/scripts/ci-timings.sh +356 -0
  52. package/package/corpus/skills/cratis-ci-pipeline-performance/scripts/fixtures/jobs/1003.json +79 -0
  53. package/package/corpus/skills/cratis-ci-pipeline-performance/scripts/fixtures/jobs/1007.json +79 -0
  54. package/package/corpus/skills/cratis-ci-pipeline-performance/scripts/fixtures/jobs/1010.json +79 -0
  55. package/package/corpus/skills/cratis-ci-pipeline-performance/scripts/fixtures/runs.json +102 -0
  56. package/package/corpus/skills/cratis-engineering-local-cross-repo-verification/LICENSE +2 -0
  57. package/package/corpus/skills/cratis-engineering-local-cross-repo-verification/SKILL.md +244 -0
  58. package/package/corpus/skills/cratis-engineering-local-cross-repo-verification/references/docker-images.md +86 -0
  59. package/package/corpus/skills/cratis-engineering-local-cross-repo-verification/references/dotnet-nuget.md +87 -0
  60. package/package/corpus/skills/cratis-engineering-local-cross-repo-verification/references/go-jvm-python-elixir.md +83 -0
  61. package/package/corpus/skills/cratis-engineering-local-cross-repo-verification/references/npm-yarn.md +78 -0
  62. package/package/corpus/skills/cratis-engineering-local-cross-repo-verification/references/other-environments.md +60 -0
  63. package/package/corpus/skills/cratis-engineering-local-cross-repo-verification/references/worked-examples.md +97 -0
  64. package/package/corpus/skills/cratis-engineering-local-cross-repo-verification/scripts/cratis-local-packages.sh +778 -0
  65. package/package/corpus/skills/cratis-engineering-local-cross-repo-verification/scripts/npm-restore-check.cjs +109 -0
  66. package/package/corpus/skills/csharp-refactoring/LICENSE +23 -0
  67. package/package/corpus/skills/csharp-refactoring/NOTICE +22 -0
  68. package/package/corpus/skills/csharp-refactoring/SKILL.md +175 -0
  69. package/package/corpus/skills/csharp-refactoring/references/operation-catalog.md +37 -0
  70. package/package/corpus/skills/directory-build-organization/LICENSE +23 -0
  71. package/package/corpus/skills/directory-build-organization/NOTICE +21 -0
  72. package/package/corpus/skills/directory-build-organization/SKILL.md +205 -0
  73. package/package/corpus/skills/directory-build-organization/references/common-patterns.md +56 -0
  74. package/package/corpus/skills/directory-build-organization/references/multi-level-examples.md +167 -0
  75. package/package/corpus/skills/directory-build-organization/references/targetframework-props-pitfall.md +54 -0
  76. package/package/corpus/skills/dotnet-aot-compat/LICENSE +23 -0
  77. package/package/corpus/skills/dotnet-aot-compat/NOTICE +18 -0
  78. package/package/corpus/skills/dotnet-aot-compat/SKILL.md +265 -0
  79. package/package/corpus/skills/dotnet-aot-compat/references/polyfills.md +43 -0
  80. package/package/corpus/skills/dotnet-trace-collect/LICENSE +23 -0
  81. package/package/corpus/skills/dotnet-trace-collect/NOTICE +65 -0
  82. package/package/corpus/skills/dotnet-trace-collect/SKILL.md +116 -0
  83. package/package/corpus/skills/dotnet-trace-collect/references/dotnet-monitor.md +97 -0
  84. package/package/corpus/skills/dotnet-trace-collect/references/dotnet-trace-collect-linux.md +84 -0
  85. package/package/corpus/skills/dotnet-trace-collect/references/dotnet-trace-collect.md +91 -0
  86. package/package/corpus/skills/dotnet-trace-collect/references/perfcollect.md +200 -0
  87. package/package/corpus/skills/dotnet-trace-collect/references/perfview.md +141 -0
  88. package/package/corpus/skills/dotnet-trace-collect/references/tool-selection.md +160 -0
  89. package/package/corpus/skills/dump-collect/LICENSE +23 -0
  90. package/package/corpus/skills/dump-collect/NOTICE +40 -0
  91. package/package/corpus/skills/dump-collect/SKILL.md +97 -0
  92. package/package/corpus/skills/dump-collect/references/container-dumps.md +412 -0
  93. package/package/corpus/skills/dump-collect/references/coreclr-dumps.md +108 -0
  94. package/package/corpus/skills/dump-collect/references/nativeaot-dumps.md +280 -0
  95. package/package/corpus/skills/eliminate-redundant-restores/LICENSE +23 -0
  96. package/package/corpus/skills/eliminate-redundant-restores/NOTICE +19 -0
  97. package/package/corpus/skills/eliminate-redundant-restores/SKILL.md +112 -0
  98. package/package/corpus/skills/extension-points/LICENSE +23 -0
  99. package/package/corpus/skills/extension-points/NOTICE +18 -0
  100. package/package/corpus/skills/extension-points/SKILL.md +248 -0
  101. package/package/corpus/skills/filter-syntax/LICENSE +23 -0
  102. package/package/corpus/skills/filter-syntax/NOTICE +17 -0
  103. package/package/corpus/skills/filter-syntax/SKILL.md +179 -0
  104. package/package/corpus/skills/including-generated-files/LICENSE +23 -0
  105. package/package/corpus/skills/including-generated-files/NOTICE +17 -0
  106. package/package/corpus/skills/including-generated-files/SKILL.md +139 -0
  107. package/package/corpus/skills/microbenchmarking/LICENSE +23 -0
  108. package/package/corpus/skills/microbenchmarking/NOTICE +36 -0
  109. package/package/corpus/skills/microbenchmarking/SKILL.md +131 -0
  110. package/package/corpus/skills/microbenchmarking/references/bdn-internals-and-tuning.md +143 -0
  111. package/package/corpus/skills/microbenchmarking/references/comparison-strategies.md +200 -0
  112. package/package/corpus/skills/microbenchmarking/references/diagnosers-and-exporters.md +150 -0
  113. package/package/corpus/skills/microbenchmarking/references/project-setup-and-running.md +189 -0
  114. package/package/corpus/skills/microbenchmarking/references/writing-benchmarks.md +310 -0
  115. package/package/corpus/skills/msbuild/LICENSE +23 -0
  116. package/package/corpus/skills/msbuild/NOTICE +55 -0
  117. package/package/corpus/skills/msbuild/SKILL.md +117 -0
  118. package/package/corpus/skills/msbuild/references/antipatterns/additional-antipatterns.md +291 -0
  119. package/package/corpus/skills/msbuild/references/antipatterns/incremental-build-inputs-outputs.md +57 -0
  120. package/package/corpus/skills/msbuild/references/antipatterns/private-assets.md +29 -0
  121. package/package/corpus/skills/msbuild/references/binlog-failure-analysis.md +85 -0
  122. package/package/corpus/skills/msbuild/references/binlog-generation.md +69 -0
  123. package/package/corpus/skills/msbuild/references/build-perf-baseline.md +493 -0
  124. package/package/corpus/skills/msbuild/references/build-perf-diagnostics.md +268 -0
  125. package/package/corpus/skills/msbuild/references/copy-to-output-directory.md +183 -0
  126. package/package/corpus/skills/msbuild/references/eval-performance.md +190 -0
  127. package/package/corpus/skills/msbuild/references/extension-points.md +281 -0
  128. package/package/corpus/skills/msbuild/references/incremental-build.md +365 -0
  129. package/package/corpus/skills/msbuild/references/msbuild-antipatterns.md +401 -0
  130. package/package/corpus/skills/orleans/LICENSE +21 -0
  131. package/package/corpus/skills/orleans/NOTICE +64 -0
  132. package/package/corpus/skills/orleans/SKILL.md +140 -0
  133. package/package/corpus/skills/orleans/references/anti-patterns.md +660 -0
  134. package/package/corpus/skills/orleans/references/configuration-api.md +933 -0
  135. package/package/corpus/skills/orleans/references/examples.md +66 -0
  136. package/package/corpus/skills/orleans/references/grain-api.md +784 -0
  137. package/package/corpus/skills/orleans/references/grains.md +75 -0
  138. package/package/corpus/skills/orleans/references/hosting.md +71 -0
  139. package/package/corpus/skills/orleans/references/implementation.md +591 -0
  140. package/package/corpus/skills/orleans/references/official-docs-index.md +250 -0
  141. package/package/corpus/skills/orleans/references/patterns.md +811 -0
  142. package/package/corpus/skills/orleans/references/persistence-api.md +495 -0
  143. package/package/corpus/skills/orleans/references/primitive-selection.md +35 -0
  144. package/package/corpus/skills/orleans/references/scheduling-and-services.md +295 -0
  145. package/package/corpus/skills/orleans/references/serialization-api.md +299 -0
  146. package/package/corpus/skills/orleans/references/streaming-api.md +396 -0
  147. package/package/corpus/skills/orleans/references/testing-patterns.md +210 -0
  148. package/package/corpus/skills/platform-detection/LICENSE +23 -0
  149. package/package/corpus/skills/platform-detection/NOTICE +17 -0
  150. package/package/corpus/skills/platform-detection/SKILL.md +230 -0
  151. package/package/corpus/skills/platform-detection/references/command-mode.md +27 -0
  152. package/package/corpus/skills/profiling/LICENSE +21 -0
  153. package/package/corpus/skills/profiling/NOTICE +50 -0
  154. package/package/corpus/skills/profiling/SKILL.md +144 -0
  155. package/package/corpus/skills/profiling/references/commands.md +439 -0
  156. package/package/corpus/skills/profiling/references/patterns.md +586 -0
  157. package/package/corpus/skills/profiling/references/profiling.md +190 -0
  158. package/package/corpus/skills/run-tests/LICENSE +23 -0
  159. package/package/corpus/skills/run-tests/NOTICE +19 -0
  160. package/package/corpus/skills/run-tests/SKILL.md +305 -0
  161. package/package/corpus/skills/target-authoring/LICENSE +23 -0
  162. package/package/corpus/skills/target-authoring/NOTICE +17 -0
  163. package/package/corpus/skills/target-authoring/SKILL.md +165 -0
  164. package/package/corpus/skills/vectorization/LICENSE +23 -0
  165. package/package/corpus/skills/vectorization/NOTICE +17 -0
  166. package/package/corpus/skills/vectorization/SKILL.md +189 -0
  167. package/package/profile-catalog.json +87 -1
  168. package/package.json +3 -2
@@ -0,0 +1,122 @@
1
+ # Async & Concurrency Patterns
2
+
3
+ ## Contents
4
+
5
+ - Don't Expose Async Wrappers for Sync Methods
6
+ - Don't Expose Sync Wrappers for Async Methods
7
+ - Use ValueTask for Hot Paths with Frequent Sync Completion
8
+ - Use Channels for Producer/Consumer
9
+ - Avoid False Sharing with Thread-Local State
10
+ - Detection
11
+ - Patterns Requiring Manual Review
12
+
13
+ ### Don't Expose Async Wrappers for Sync Methods
14
+ 🟡 **AVOID** wrapping sync methods with `Task.Run` in libraries | .NET Core+
15
+
16
+ ❌
17
+ ```csharp
18
+ public Task<int> ComputeHashAsync(byte[] data) =>
19
+ Task.Run(() => ComputeHash(data));
20
+ ```
21
+ ✅
22
+ ```csharp
23
+ public int ComputeHash(byte[] data) { /* CPU-bound work */ }
24
+ // Consumer decides: var hash = await Task.Run(() => lib.ComputeHash(data));
25
+ ```
26
+
27
+ **Impact: Eliminates unnecessary thread pool queue/dequeue overhead per call.**
28
+
29
+ ### Don't Expose Sync Wrappers for Async Methods
30
+ 🟡 **AVOID** creating sync wrappers that block on async implementations | .NET Core+
31
+
32
+ ❌
33
+ ```csharp
34
+ public string GetData() => GetDataAsync().Result;
35
+ ```
36
+ ✅
37
+ ```csharp
38
+ public async Task<string> GetDataAsync() { /* ... */ }
39
+ ```
40
+
41
+ **Impact: Prevents deadlocks and thread pool starvation from hidden sync-over-async blocking.**
42
+
43
+ ### Use ValueTask for Hot Paths with Frequent Sync Completion
44
+ 🟡 **DO** use `ValueTask<T>` on hot paths where sync completion is common | .NET Core 2.1+
45
+
46
+ ❌
47
+ ```csharp
48
+ public async Task<int> ReadAsync(Memory<byte> buffer)
49
+ {
50
+ if (_bufferedCount > 0)
51
+ return ReadFromBuffer(buffer.Span);
52
+ return await ReadAsyncCore(buffer);
53
+ }
54
+ ```
55
+ ✅
56
+ ```csharp
57
+ public ValueTask<int> ReadAsync(Memory<byte> buffer)
58
+ {
59
+ if (_bufferedCount > 0)
60
+ return new ValueTask<int>(ReadFromBuffer(buffer.Span));
61
+ return new ValueTask<int>(ReadAsyncCore(buffer));
62
+ }
63
+ ```
64
+
65
+ **Impact: Eliminates Task\<T\> allocation on synchronous completion — the struct stores results inline.**
66
+
67
+ ### Use Channels for Producer/Consumer
68
+ 🟡 **DO** use `System.Threading.Channels` for producer-consumer patterns | .NET Core 3.0+
69
+
70
+ ❌
71
+ ```csharp
72
+ var queue = new BlockingCollection<WorkItem>();
73
+ var item = queue.Take();
74
+ ```
75
+ ✅
76
+ ```csharp
77
+ var channel = Channel.CreateUnbounded<WorkItem>();
78
+
79
+ // Producer
80
+ await channel.Writer.WriteAsync(item);
81
+
82
+ // Consumer
83
+ await foreach (var item in channel.Reader.ReadAllAsync())
84
+ Process(item);
85
+ ```
86
+
87
+ **Impact: ~25% faster, ~95% fewer GC collections vs manual approaches.**
88
+
89
+ ### Avoid False Sharing with Thread-Local State
90
+ 🟡 **AVOID** adjacent mutable fields written by different threads | .NET 7+
91
+
92
+ ❌
93
+ ```csharp
94
+ class SharedCounters
95
+ {
96
+ public long Counter1;
97
+ public long Counter2;
98
+ }
99
+ ```
100
+ ✅
101
+ ```csharp
102
+ [StructLayout(LayoutKind.Explicit, Size = 128)]
103
+ struct PaddedCounter
104
+ {
105
+ [FieldOffset(0)] public long Value;
106
+ }
107
+ ```
108
+
109
+ **Impact: Eliminates cross-core cache invalidation — can improve multi-threaded throughput by 10x+.**
110
+
111
+ ## Detection
112
+
113
+ Scan recipes for async anti-patterns. Run these and report exact counts.
114
+
115
+ ```bash
116
+ # async void methods (correctness issue — crashes on exception)
117
+ grep -rn --include='*.cs' 'async void' --exclude-dir=bin --exclude-dir=obj . | grep -v 'event' | wc -l
118
+ ```
119
+
120
+ ### Patterns Requiring Manual Review
121
+
122
+ - **Sync-over-async** (`.Result`, `.Wait()`): `.Result` matches any property named Result — needs type context to confirm it's `Task.Result`
@@ -0,0 +1,220 @@
1
+ # Collections & LINQ Patterns
2
+
3
+ ## Contents
4
+
5
+ - Use FrozenDictionary/FrozenSet for Read-Heavy Lookup Tables
6
+ - Use Dictionary Alternate Lookup for Span-Based Keys
7
+ - Use CollectionsMarshal.GetValueRefOrNullRef for Lookup-and-Update
8
+ - Use Collection Expressions [] for Zero-Allocation Span Creation
9
+ - Use EnsureCapacity on List/Stack/Queue Before Bulk Adds
10
+ - Use TryGetNonEnumeratedCount for Pre-Sizing
11
+ - Hoist Static Data Out of Method Bodies
12
+ - Add Overloads to Avoid params Array Allocation
13
+ - Detection
14
+ - Patterns Requiring Manual Review
15
+
16
+ ### Use FrozenDictionary/FrozenSet for Read-Heavy Lookup Tables
17
+ 🟡 **DO** use `FrozenDictionary`/`FrozenSet` for collections created once and read many times | .NET 8+
18
+
19
+ ❌
20
+ ```csharp
21
+ private static readonly Dictionary<string, int> s_statusCodes = new()
22
+ {
23
+ ["OK"] = 200, ["NotFound"] = 404, ["InternalServerError"] = 500
24
+ };
25
+ ```
26
+ ✅
27
+ ```csharp
28
+ private static readonly FrozenDictionary<string, int> s_statusCodes =
29
+ new Dictionary<string, int>
30
+ {
31
+ ["OK"] = 200, ["NotFound"] = 404, ["InternalServerError"] = 500
32
+ }.ToFrozenDictionary();
33
+ ```
34
+
35
+ **Impact: ~50% faster lookups than Dictionary, ~14x faster than ImmutableDictionary.**
36
+
37
+ ### Use Dictionary Alternate Lookup for Span-Based Keys
38
+ 🟡 **DO** use `GetAlternateLookup<ReadOnlySpan<char>>()` to avoid string allocation on lookups | .NET 9+
39
+
40
+ ❌
41
+ ```csharp
42
+ string key = headerLine.Substring(0, colonIndex);
43
+ if (s_dict.TryGetValue(key, out int value)) { /* ... */ }
44
+ ```
45
+ ✅
46
+ ```csharp
47
+ var lookup = s_dict.GetAlternateLookup<ReadOnlySpan<char>>();
48
+ ReadOnlySpan<char> key = headerLine.AsSpan(0, colonIndex);
49
+ if (lookup.TryGetValue(key, out int value)) { }
50
+ ```
51
+
52
+ **Impact: Avoids string allocation per lookup — especially valuable in parser/protocol hot paths.**
53
+
54
+ ### Use CollectionsMarshal.GetValueRefOrNullRef for Lookup-and-Update
55
+ 🟡 **DO** use `CollectionsMarshal.GetValueRefOrAddDefault` for dictionary update patterns | .NET 6+
56
+
57
+ ❌
58
+ ```csharp
59
+ _counts.TryGetValue(key, out int count);
60
+ _counts[key] = count + 1;
61
+ ```
62
+ ✅
63
+ ```csharp
64
+ ref int count = ref CollectionsMarshal.GetValueRefOrAddDefault(_counts, key, out _);
65
+ count++;
66
+ ```
67
+
68
+ **Impact: ~48% faster for lookup-and-update patterns (95µs → 49µs).**
69
+
70
+ ### Use Collection Expressions [] for Zero-Allocation Span Creation
71
+ 🟡 **DO** use collection expressions for `Span<T>` targets | C# 12 / .NET 8+
72
+
73
+ ❌
74
+ ```csharp
75
+ int[] values = new int[] { a, b, c, d };
76
+ ```
77
+ ✅
78
+ ```csharp
79
+ Span<int> values = [a, b, c, d];
80
+ ReadOnlySpan<int> daysInMonth = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
81
+ ```
82
+
83
+ **Impact: Zero heap allocation for span-targeted collection expressions.**
84
+
85
+ ### Use EnsureCapacity on List/Stack/Queue Before Bulk Adds
86
+ 🟡 **DO** call `EnsureCapacity` before bulk insertions | .NET 6+
87
+
88
+ ❌
89
+ ```csharp
90
+ var list = new List<int>();
91
+ for (int i = 0; i < 10000; i++)
92
+ list.Add(i);
93
+ ```
94
+ ✅
95
+ ```csharp
96
+ var list = new List<int>();
97
+ list.EnsureCapacity(10000);
98
+ for (int i = 0; i < 10000; i++)
99
+ list.Add(i);
100
+ ```
101
+
102
+ **Impact: Reduces reallocations and array copies during bulk operations.**
103
+
104
+ ### Use TryGetNonEnumeratedCount for Pre-Sizing
105
+ 🟡 **DO** use `TryGetNonEnumeratedCount` to pre-size destination collections | .NET 6+
106
+
107
+ ❌
108
+ ```csharp
109
+ var results = new List<int>();
110
+ foreach (var item in source)
111
+ results.Add(Transform(item));
112
+ ```
113
+ ✅
114
+ ```csharp
115
+ var results = source.TryGetNonEnumeratedCount(out int count)
116
+ ? new List<int>(count)
117
+ : new List<int>();
118
+ foreach (var item in source)
119
+ results.Add(Transform(item));
120
+ ```
121
+
122
+ **Impact: Avoids O(n) enumeration for counting; eliminates resizing allocations.**
123
+
124
+ ### Hoist Static Data Out of Method Bodies
125
+ 🟡 **AVOID** creating collections with static/deterministic data inside method bodies | .NET Core+
126
+
127
+ ❌
128
+ ```csharp
129
+ public string Convert(long number)
130
+ {
131
+ var groupsMap = new Dictionary<long, Func<long, string>>
132
+ {
133
+ { 1_000_000_000, n => $"{Convert(n)} billion" },
134
+ { 1_000_000, n => $"{Convert(n)} million" },
135
+ { 1_000, n => $"{Convert(n)} thousand" },
136
+ };
137
+ }
138
+ ```
139
+ ✅
140
+ ```csharp
141
+ private static readonly FrozenDictionary<long, Func<long, string>> s_groupsMap =
142
+ new Dictionary<long, Func<long, string>>
143
+ {
144
+ { 1_000_000_000, n => $"{Convert(n)} billion" },
145
+ { 1_000_000, n => $"{Convert(n)} million" },
146
+ { 1_000, n => $"{Convert(n)} thousand" },
147
+ }.ToFrozenDictionary();
148
+
149
+ public string Convert(long number)
150
+ {
151
+ // ... use s_groupsMap
152
+ }
153
+ ```
154
+
155
+ **Impact: Eliminates collection + internal storage + closure allocations per call. For a Dictionary with N entries, saves ~N+3 allocations per invocation.**
156
+
157
+ ### Add Overloads to Avoid params Array Allocation
158
+ 🟡 **DO** add 1- and 2-argument overloads for methods that accept `params T[]`, or use `params ReadOnlySpan<T>` on .NET 9+ | .NET Core+
159
+
160
+ ❌
161
+ ```csharp
162
+ public static string Transform(this string input, params IStringTransformer[] transformers) =>
163
+ transformers.Aggregate(input, (current, t) => t.Transform(current));
164
+
165
+ "hello".Transform(To.TitleCase);
166
+ ```
167
+ ✅
168
+ ```csharp
169
+ // Option A: Explicit overloads for common arities
170
+ public static string Transform(this string input, IStringTransformer transformer) =>
171
+ transformer.Transform(input);
172
+
173
+ public static string Transform(this string input, IStringTransformer t1, IStringTransformer t2) =>
174
+ t2.Transform(t1.Transform(input));
175
+
176
+ public static string Transform(this string input, params IStringTransformer[] transformers) =>
177
+ transformers.Aggregate(input, (current, t) => t.Transform(current));
178
+
179
+ // Option B (.NET 9+ / C# 13): params ReadOnlySpan<T> — zero heap allocation
180
+ public static string Transform(this string input, params ReadOnlySpan<IStringTransformer> transformers)
181
+ {
182
+ foreach (var t in transformers)
183
+ input = t.Transform(input);
184
+ return input;
185
+ }
186
+ ```
187
+
188
+ **Impact: Eliminates one array allocation per call for the common 1- and 2-argument cases. `params ReadOnlySpan<T>` eliminates it for all arities.**
189
+
190
+ ## Detection
191
+
192
+ Scan recipes for collection and LINQ anti-patterns. Run these and report exact counts.
193
+
194
+ ```bash
195
+ # Static Dictionary not using FrozenDictionary (read-only after init)
196
+ grep -rn --include='*.cs' 'static readonly Dictionary<' --exclude-dir=bin --exclude-dir=obj . | wc -l
197
+
198
+ # Static FrozenDictionary (already optimized — verify the inverse)
199
+ grep -rn --include='*.cs' 'static readonly FrozenDictionary<' --exclude-dir=bin --exclude-dir=obj . | wc -l
200
+
201
+ # Per-call List allocation (inside method bodies, not static/readonly fields)
202
+ grep -rn --include='*.cs' 'new List<' --exclude-dir=bin --exclude-dir=obj . | grep -v 'static\|readonly' | wc -l
203
+
204
+ # Per-call Dictionary allocation (inside method bodies, not static/readonly fields)
205
+ grep -rn --include='*.cs' 'new Dictionary<' --exclude-dir=bin --exclude-dir=obj . | grep -v 'static\|readonly' | wc -l
206
+
207
+ # StringComparer.CurrentCulture usage (almost always wrong in library code — use Ordinal)
208
+ grep -rn --include='*.cs' 'StringComparer.CurrentCulture' --exclude-dir=bin --exclude-dir=obj . | wc -l
209
+
210
+ # LINQ chains in extension/hot-path files (.Select, .Where, .Cast, .Take, .Aggregate)
211
+ grep -rn --include='*.cs' -E '\.(Select|Where|Cast|Take|Aggregate)\(' --exclude-dir=bin --exclude-dir=obj . | wc -l
212
+ ```
213
+
214
+ For the LINQ chain recipe: any hit in a file whose name ends in `Extensions.cs`, `Formatter.cs`, or implements a method called from a public extension method is a hot-path candidate. Inspect each hit in these files and flag LINQ chains that allocate delegates, enumerators, or intermediate collections on every call. Hits in localization converters or one-time initialization are lower priority.
215
+
216
+ ### Patterns Requiring Manual Review
217
+
218
+ - **ContainsKey + indexer double-lookup**: Requires verifying the same key is used in a subsequent indexer access — multi-line/multi-statement context
219
+ - **LINQ on hot paths**: The LINQ chain recipe above catches call sites, but distinguishing hot-path from cold-path requires context. Prioritize hits in `*Extensions.cs` and `*Formatter.cs` files, which are typically called on every user invocation
220
+ - **`new Dictionary/List<` in method bodies vs fields**: The grep heuristic (`grep -v 'static\|readonly'`) catches most cases but may include false positives from field initializers without `static`/`readonly` — spot-check flagged lines
@@ -0,0 +1,291 @@
1
+ # Critical .NET Performance Anti-Patterns
2
+
3
+ ## Contents
4
+
5
+ - Async / Tasks
6
+ - Memory / Allocation
7
+ - Strings
8
+ - Regular Expressions
9
+ - JSON Serialization
10
+ - Networking
11
+ - General
12
+ - Detection
13
+
14
+ 17 patterns that cause deadlocks, order-of-magnitude regressions, or excessive allocations.
15
+
16
+ ## Async / Tasks
17
+
18
+ ### Never Block on Async (Sync-over-Async)
19
+ 🔴 **AVOID** | .NET Core+
20
+
21
+ ❌
22
+ ```csharp
23
+ public string GetData()
24
+ => GetDataAsync().Result;
25
+ ```
26
+ ✅
27
+ ```csharp
28
+ public async Task<string> GetDataAsync()
29
+ => await GetDataInternalAsync();
30
+ ```
31
+ **Impact: Deadlocks or thread pool starvation; wastes threads, destroys scalability.**
32
+
33
+ ### Never Await a ValueTask Multiple Times
34
+ 🔴 **AVOID** | .NET Core 2.1+
35
+
36
+ ❌
37
+ ```csharp
38
+ ValueTask<int> vt = SomeMethodAsync();
39
+ int a = await vt;
40
+ int b = await vt;
41
+ ```
42
+ ✅
43
+ ```csharp
44
+ int result = await SomeMethodAsync();
45
+ ```
46
+ **Impact: Undefined behavior — silent data corruption or exceptions.**
47
+
48
+ ## Memory / Allocation
49
+
50
+ ### Use Span\<T\> / AsSpan Instead of Substring for Slicing
51
+ 🔴 **DO** | .NET Core 2.1+
52
+
53
+ ❌
54
+ ```csharp
55
+ string sub = input.Substring(5, 10);
56
+ ```
57
+ ✅
58
+ ```csharp
59
+ ReadOnlySpan<char> sub = input.AsSpan(5, 10);
60
+ ```
61
+ **Impact: Eliminates per-slice allocations; 2-4x faster via vectorization.**
62
+
63
+ ### Use ArrayPool\<T\> for Temporary Buffers
64
+ 🔴 **DO** | .NET Core+
65
+
66
+ ❌
67
+ ```csharp
68
+ byte[] buf = new byte[4096];
69
+ ```
70
+ ✅
71
+ ```csharp
72
+ byte[] buf = ArrayPool<byte>.Shared.Rent(4096);
73
+ Process(buf);
74
+ ArrayPool<byte>.Shared.Return(buf);
75
+ ```
76
+ **Impact: Dramatically reduces GC pressure for buffer-heavy workloads.**
77
+
78
+ ### Avoid stackalloc in Loops
79
+ 🔴 **AVOID** | .NET 5+
80
+
81
+ ❌
82
+ ```csharp
83
+ for (int i = 0; i < 10_000; i++)
84
+ Span<byte> buf = stackalloc byte[1024];
85
+ ```
86
+ ✅
87
+ ```csharp
88
+ Span<byte> buf = stackalloc byte[1024];
89
+ for (int i = 0; i < 10_000; i++) { Process(buf); }
90
+ ```
91
+ **Impact: StackOverflowException — unrecoverable, no catch possible.**
92
+
93
+ ### Avoid Boxing Value Types
94
+ 🔴 **AVOID** | .NET 6+
95
+
96
+ ❌
97
+ ```csharp
98
+ string s = string.Format("{0}.{1}", major, minor);
99
+ ```
100
+ ✅
101
+ ```csharp
102
+ string s = $"{major}.{minor}";
103
+ ```
104
+ **Impact: When replacing `string.Format` with C# 10+ interpolation, typical improvements are ~40% faster with significantly less allocation. Actual gains vary by call site.**
105
+
106
+ ## Strings
107
+
108
+ ### Use StringComparison.Ordinal for Non-Linguistic Comparisons
109
+ 🔴 **DO** | .NET Core+
110
+
111
+ ❌
112
+ ```csharp
113
+ bool found = text.IndexOf("Content-Type") >= 0;
114
+ ```
115
+ ✅
116
+ ```csharp
117
+ bool found = text.Contains("Content-Type", StringComparison.Ordinal);
118
+ ```
119
+ **Impact: 2-3x faster; OrdinalIgnoreCase hash codes ~3.3x faster.**
120
+
121
+ ### Use AsSpan Instead of Substring
122
+ 🔴 **DO** | .NET Core 2.1+
123
+
124
+ ❌
125
+ ```csharp
126
+ int val = int.Parse(str.Substring(5, 3));
127
+ ```
128
+ ✅
129
+ ```csharp
130
+ int val = int.Parse(str.AsSpan(5, 3));
131
+ ```
132
+ **Impact: Eliminates one string allocation per parse operation.**
133
+
134
+ ## Regular Expressions
135
+
136
+ ### Use Source-Generated Regex [GeneratedRegex]
137
+ 🔴 **ALWAYS** use `[GeneratedRegex]` for all static regex patterns | .NET 7+
138
+
139
+ ❌
140
+ ```csharp
141
+ private static readonly Regex s_re =
142
+ new(@"\w+@\w+\.\w+", RegexOptions.Compiled);
143
+ ```
144
+ ✅
145
+ ```csharp
146
+ [GeneratedRegex(@"\w+@\w+\.\w+")]
147
+ private static partial Regex EmailRegex();
148
+ ```
149
+ **Impact: Always beneficial or neutral for static patterns — near-zero startup, better throughput, and required for AOT/trimming scenarios.**
150
+
151
+ ### Avoid Nested Quantifiers (Catastrophic Backtracking)
152
+ 🔴 **AVOID** | .NET Core+
153
+
154
+ ❌
155
+ ```csharp
156
+ var r = new Regex(@"^(\w+)+$");
157
+ ```
158
+ ✅
159
+ ```csharp
160
+ var r = new Regex(@"^\w+$", RegexOptions.NonBacktracking);
161
+ ```
162
+ **Impact: Can hang process indefinitely on crafted input.**
163
+
164
+ ### Use TryGetValue Instead of ContainsKey + Indexer
165
+ 🔴 **DO** | .NET Core+
166
+
167
+ ❌
168
+ ```csharp
169
+ if (dict.ContainsKey(key))
170
+ Use(dict[key]);
171
+ ```
172
+ ✅
173
+ ```csharp
174
+ if (dict.TryGetValue(key, out var value))
175
+ Use(value);
176
+ ```
177
+ **Impact: ~2x faster (50% reduction in lookup time).**
178
+
179
+ ### Avoid LINQ in Hot Paths
180
+ 🔴 **AVOID** | .NET Core+
181
+
182
+ ❌
183
+ ```csharp
184
+ bool found = items.Any(x => x.Name == target);
185
+ ```
186
+ ✅
187
+ ```csharp
188
+ bool found = false;
189
+ foreach (var item in items)
190
+ if (item.Name == target) { found = true; break; }
191
+ ```
192
+ **Impact: Eliminates 1-3 allocations per call; measurable in tight loops.**
193
+
194
+ ### Don't Iterate IEnumerable Multiple Times
195
+ 🔴 **AVOID** | .NET Core+
196
+
197
+ ❌
198
+ ```csharp
199
+ foreach (Type t in types) { Validate(t); }
200
+ _types = types.ToArray();
201
+ ```
202
+ ✅
203
+ ```csharp
204
+ Type[] arr = types.ToArray();
205
+ foreach (Type t in arr) { Validate(t); }
206
+ _types = arr;
207
+ ```
208
+ **Impact: Halves enumeration cost; prevents bugs from re-executing deferred queries.**
209
+
210
+ ## JSON Serialization
211
+
212
+ ### Use System.Text.Json Source Generator
213
+ 🔴 **DO** | .NET 6+
214
+
215
+ ❌
216
+ ```csharp
217
+ string json = JsonSerializer.Serialize(post);
218
+ ```
219
+ ✅
220
+ ```csharp
221
+ [JsonSerializable(typeof(BlogPost))]
222
+ internal partial class AppJsonCtx : JsonSerializerContext { }
223
+ string json = JsonSerializer.Serialize(post, AppJsonCtx.Default.BlogPost);
224
+ ```
225
+ **Impact: 37-44% faster; enables trimming and Native AOT.**
226
+
227
+ ### Cache JsonSerializerOptions
228
+ 🔴 **DO** | .NET 5+
229
+
230
+ ❌
231
+ ```csharp
232
+ JsonSerializer.Serialize(obj, new JsonSerializerOptions());
233
+ ```
234
+ ✅
235
+ ```csharp
236
+ private static readonly JsonSerializerOptions s_opts = new();
237
+ JsonSerializer.Serialize(obj, s_opts);
238
+ ```
239
+ **Impact: Up to 592x slower without caching (.NET 6); always cache or use defaults.**
240
+
241
+ ## Networking
242
+
243
+ ### Reuse HttpClient Instances
244
+ 🔴 **DO** | .NET Core 2.1+
245
+
246
+ ❌
247
+ ```csharp
248
+ using var client = new HttpClient();
249
+ await client.GetStringAsync(url);
250
+ ```
251
+ ✅
252
+ ```csharp
253
+ private static readonly HttpClient s_http = new(new SocketsHttpHandler
254
+ { PooledConnectionLifetime = TimeSpan.FromMinutes(5) });
255
+ await s_http.GetStringAsync(url);
256
+ ```
257
+ **Impact: Prevents socket exhaustion; 6-12x faster concurrent HTTPS.**
258
+
259
+ ## General
260
+
261
+ ### Use SearchValues\<T\> for Repeated Set Searches
262
+ 🔴 **DO** | .NET 8+
263
+
264
+ ❌
265
+ ```csharp
266
+ int pos = text.IndexOfAny("ABCDEF".ToCharArray());
267
+ ```
268
+ ✅
269
+ ```csharp
270
+ private static readonly SearchValues<char> s_hex = SearchValues.Create("ABCDEF");
271
+ int pos = text.AsSpan().IndexOfAny(s_hex);
272
+ ```
273
+ **Impact: 2-10x faster for chars; 10-30x faster for multi-string (.NET 9+).**
274
+
275
+ ## Detection
276
+
277
+ Scan recipes for critical anti-patterns. Run these and report exact counts of issues found in each case.
278
+
279
+ ```bash
280
+ # .IndexOf(string) without StringComparison (culture-aware, 2-3x slower)
281
+ grep -rn --include='*.cs' -E '\.IndexOf\("[^"]+"\)' --exclude-dir=bin --exclude-dir=obj . | wc -l
282
+
283
+ # .Substring( calls (allocates new string — consider AsSpan)
284
+ grep -rn --include='*.cs' '\.Substring(' --exclude-dir=bin --exclude-dir=obj . | wc -l
285
+
286
+ # .StartsWith/.EndsWith without StringComparison (culture-aware, 2-3x slower)
287
+ grep -rn --include='*.cs' -E '\.(StartsWith|EndsWith)\("[^"]+"\)' --exclude-dir=bin --exclude-dir=obj . | wc -l
288
+
289
+ # .Contains(string) without StringComparison — NOTE: will also match collection .Contains() calls; filter to string receivers
290
+ grep -rn --include='*.cs' -E '\.Contains\("[^"]+"\)' --exclude-dir=bin --exclude-dir=obj . | wc -l
291
+ ```