Skill v1.0.1
currentAutomated scan100/100+4 new
version: "1.0.1" name: csharp description: "Language-specific super-code guidelines for csharp." risk: safe source: community date_added: "2026-06-16"
C#: Idiomatic Efficiency Reference
Apply only within the requested scope and current project conventions. Preserve behavior, ownership, error handling, accessibility and security; examples are independent fragments, not equivalent rewrites in every context. Verify language/library versions and run relevant checks. Required tests and user-facing progress/results take precedence over brevity rules. Do not install dependencies or delete files merely to follow these examples.
When to Use
- Use this skill when the task matches this description: Language-specific super-code guidelines for csharp.
Table of Contents
- LINQ & Collections
- Null Handling
- Async/Await
- Records & Pattern Matching
- Error Handling
- Resource Management
- Anti-patterns specific to C#
1. LINQ & Collections {#linq}
// ❌ Imperative accumulationvar result = new List<string>();foreach (var item in items) {if (item.IsActive) result.Add(item.Name.ToUpper());}// ✅var result = items.Where(i => i.IsActive).Select(i => i.Name.ToUpper()).ToList();
// ❌ Manual groupingvar grouped = new Dictionary<string, List<Item>>();foreach (var item in items) {if (!grouped.ContainsKey(item.Category))grouped[item.Category] = new List<Item>();grouped[item.Category].Add(item);}// ✅var grouped = items.GroupBy(i => i.Category).ToDictionary(g => g.Key, g => g.ToList());
// ❌ Checking Any() then First()if (items.Any(i => i.IsValid)) {var first = items.First(i => i.IsValid);}// ✅var first = items.FirstOrDefault(i => i.IsValid);if (first is not null) { ... }
Prefer method syntax for chains of 2+ operations. Query syntax is fine for complex joins.
2. Null Handling {#nulls}
// ❌ Nested null checksstring city = null;if (user != null && user.Address != null) {city = user.Address.City;}// ✅var city = user?.Address?.City;
// ❌ Ternary for null fallbackvar name = user != null ? user.Name : "Unknown";// ✅var name = user?.Name ?? "Unknown";
// ❌ Null check before event invocationif (OnChanged != null) OnChanged(this, args);// ✅OnChanged?.Invoke(this, args);
// ❌ Throwing ArgumentNullException manuallyif (name == null) throw new ArgumentNullException(nameof(name));// ✅ (.NET 6+)ArgumentNullException.ThrowIfNull(name);
Enable nullable reference types (`<Nullable>enable</Nullable>`) project-wide.
3. Async/Await {#async}
// ❌ Blocking on async codevar result = GetDataAsync().Result; // deadlock risk// ✅var result = await GetDataAsync();
// ❌ async void (exceptions are unobservable)async void OnButtonClick() { await DoWork(); }// ✅ — async Task; only async void for event handlers that truly require itasync Task OnButtonClick() { await DoWork(); }
// ❌ Sequential awaits for independent workvar a = await FetchA();var b = await FetchB();// ✅var (a, b) = (await Task.WhenAll(FetchA(), FetchB())) switch{var r => (r[0], r[1])};// or cleaner with ValueTuple:var taskA = FetchA();var taskB = FetchB();var a = await taskA;var b = await taskB;
// ❌ Wrapping synchronous code in Task.Run inside a librarypublic Task<int> GetValue() => Task.Run(() => ComputeSync());// ✅ — let the caller decide; expose sync methodpublic int GetValue() => ComputeSync();
Add `ConfigureAwait(false)` in library code. Omit in app/UI code.
4. Records & Pattern Matching {#records}
// ❌ Manual equality, ToString, Deconstruct for data typesclass Point {public int X { get; init; }public int Y { get; init; }// + Equals, GetHashCode, ToString...}// ✅ (C# 9+)record Point(int X, int Y);
// ❌ if-else chain for type checkingif (shape is Circle) {var c = (Circle)shape;return c.Radius * c.Radius * Math.PI;} else if (shape is Rectangle) { ... }// ✅return shape switch {Circle { Radius: var r } => r * r * Math.PI,Rectangle { Width: var w, Height: var h } => w * h,_ => throw new ArgumentException($"Unknown shape: {shape}")};
// ❌ Range checking with &&if (score >= 0 && score <= 100) { ... }// ✅ (C# 9+)if (score is >= 0 and <= 100) { ... }
5. Error Handling {#errors}
// ❌ Catching Exception to log and swallowtry { Process(); }catch (Exception ex) { logger.LogError(ex, "error"); }// ✅ — catch specific, rethrow if you can't handletry { Process(); }catch (HttpRequestException ex) {throw new ServiceException("upstream failure", ex);}
// ❌ throw ex (resets stack trace)catch (Exception ex) { throw ex; }// ✅catch (Exception ex) { throw; } // preserves stack trace// or wrap: throw new AppException("context", ex);
// ❌ Exceptions for flow controltry { return dict[key]; }catch (KeyNotFoundException) { return defaultValue; }// ✅return dict.TryGetValue(key, out var value) ? value : defaultValue;
6. Resource Management {#resources}
// ❌ Manual Disposevar conn = new SqlConnection(cs);conn.Open();// ... use conn ...conn.Dispose(); // missed on exception// ✅using var conn = new SqlConnection(cs);conn.Open();// disposed at end of scope
// ❌ Verbose using blockusing (var reader = new StreamReader(path)) {return reader.ReadToEnd();}// ✅ (C# 8+)using var reader = new StreamReader(path);return reader.ReadToEnd();// or just: return File.ReadAllText(path);
7. Anti-patterns specific to C# {#antipatterns}
| Anti-pattern | Preferred | |
|---|---|---|
string.Format("{0}", x) | $"{x}" string interpolation | |
List<T> as public API return | IReadOnlyList<T> or IEnumerable<T> | |
async void | async Task | |
.Result / .Wait() on Task | await | |
throw ex | throw (preserves stack trace) | |
Manual IEquatable on data types | record | |
object parameters | generics with constraints | |
DateTime.Now | DateTime.UtcNow or DateTimeOffset | |
| Mutable public fields | properties with { get; set; } or { get; init; } | |
catch (Exception) { } (swallow all) | catch specific exceptions, rethrow unknown | |
IDisposable without using | using declaration |
Limitations
- These are language-specific guidelines and do not cover overall architectural decisions.
- Over-compression might reduce readability; apply judgement.