jpskill.com
🛠️ 開発・MCP コミュニティ

type-design-performance

.NETでパフォーマンスの高い型を設計するために、クラスの密封、readonly構造体の利用、静的純粋関数の優先、早期列挙の回避、適切なコレクション型の選択などを行うSkill。

📜 元の英語説明(参考)

Design .NET types for performance. Seal classes, use readonly structs, prefer static pure functions, avoid premature enumeration, and choose the right collection types.

🇯🇵 日本人クリエイター向け解説

一言でいうと

.NETでパフォーマンスの高い型を設計するために、クラスの密封、readonly構造体の利用、静的純粋関数の優先、早期列挙の回避、適切なコレクション型の選択などを行うSkill。

※ jpskill.com 編集部が日本のビジネス現場向けに補足した解説です。Skill本体の挙動とは独立した参考情報です。

⚡ おすすめ: コマンド1行でインストール(60秒)

下記のコマンドをコピーしてターミナル(Mac/Linux)または PowerShell(Windows)に貼り付けてください。 ダウンロード → 解凍 → 配置まで全自動。

🍎 Mac / 🐧 Linux
mkdir -p ~/.claude/skills && cd ~/.claude/skills && curl -L -o type-design-performance.zip https://jpskill.com/download/8725.zip && unzip -o type-design-performance.zip && rm type-design-performance.zip
🪟 Windows (PowerShell)
$d = "$env:USERPROFILE\.claude\skills"; ni -Force -ItemType Directory $d | Out-Null; iwr https://jpskill.com/download/8725.zip -OutFile "$d\type-design-performance.zip"; Expand-Archive "$d\type-design-performance.zip" -DestinationPath $d -Force; ri "$d\type-design-performance.zip"

完了後、Claude Code を再起動 → 普通に「動画プロンプト作って」のように話しかけるだけで自動発動します。

💾 手動でダウンロードしたい(コマンドが難しい人向け)
  1. 1. 下の青いボタンを押して type-design-performance.zip をダウンロード
  2. 2. ZIPファイルをダブルクリックで解凍 → type-design-performance フォルダができる
  3. 3. そのフォルダを C:\Users\あなたの名前\.claude\skills\(Win)または ~/.claude/skills/(Mac)へ移動
  4. 4. Claude Code を再起動

⚠️ ダウンロード・利用は自己責任でお願いします。当サイトは内容・動作・安全性について責任を負いません。

🎯 このSkillでできること

下記の説明文を読むと、このSkillがあなたに何をしてくれるかが分かります。Claudeにこの分野の依頼をすると、自動で発動します。

📦 インストール方法 (3ステップ)

  1. 1. 上の「ダウンロード」ボタンを押して .skill ファイルを取得
  2. 2. ファイル名の拡張子を .skill から .zip に変えて展開(macは自動展開可)
  3. 3. 展開してできたフォルダを、ホームフォルダの .claude/skills/ に置く
    • · macOS / Linux: ~/.claude/skills/
    • · Windows: %USERPROFILE%\.claude\skills\

Claude Code を再起動すれば完了。「このSkillを使って…」と話しかけなくても、関連する依頼で自動的に呼び出されます。

詳しい使い方ガイドを見る →
最終更新
2026-05-18
取得日時
2026-05-18
同梱ファイル
1

📖 Skill本文(日本語訳)

※ 原文(英語/中国語)を Gemini で日本語化したものです。Claude 自身は原文を読みます。誤訳がある場合は原文をご確認ください。

パフォーマンスのための型設計

この Skill を使用する場面

この Skill は、以下のような場合に使用します。

  • 新しい型や API を設計する
  • コードのパフォーマンス問題をレビューする
  • classstructrecord のいずれかを選択する
  • コレクションや列挙型を扱う

中核となる原則

  1. 型を sealed にする - 明示的に継承のために設計されている場合を除きます
  2. readonly struct を優先する - 小さく、不変な値型の場合
  3. static pure function を優先する - パフォーマンスとテスト容易性が向上します
  4. 列挙を遅延する - 必要になるまで具体化しないでください
  5. 不変なコレクションを返す - API の境界から

クラスをデフォルトで Seal する

クラスを sealing することで、JIT の非仮想化が可能になり、API の意図が伝わります。

// DO: 継承のために設計されていないクラスを Seal する
public sealed class OrderProcessor
{
    public void Process(Order order) { }
}

// DO: レコードを Seal する (レコードはクラスです)
public sealed record OrderCreated(OrderId Id, CustomerId CustomerId);

// DON'T: 理由もなく unsealed のままにしない
public class OrderProcessor  // サブクラス化可能 - 意図的ですか?
{
    public virtual void Process(Order order) { }  // Virtual = 遅くなる
}

利点:

  • JIT はメソッド呼び出しを非仮想化できます
  • 「これは拡張ポイントではありません」ということを伝えます
  • 意図しない破壊的変更を防ぎます

値型のための Readonly Struct

Struct は、不変である場合は readonly にする必要があります。これにより、防御的コピーを防ぎます。

// DO: 不変な値型のための Readonly struct
public readonly record struct OrderId(Guid Value)
{
    public static OrderId New() => new(Guid.NewGuid());
    public override string ToString() => Value.ToString();
}

// DO: 小さく、短命なデータのための Readonly struct
public readonly struct Money
{
    public decimal Amount { get; }
    public string Currency { get; }

    public Money(decimal amount, string currency)
    {
        Amount = amount;
        Currency = currency;
    }
}

// DON'T: 可変な struct (防御的コピーを引き起こす)
public struct Point  // Not readonly!
{
    public int X { get; set; }  // Mutable!
    public int Y { get; set; }
}

Struct を使用する場面

Struct を使用する場面 Class を使用する場面
小さい (通常 ≤16 バイト) より大きなオブジェクト
短命 長命
頻繁に割り当てられる 共有参照が必要
値セマンティクスが必要 識別セマンティクスが必要
不変 可変な状態

Static Pure Function を優先する

副作用のない static メソッドは、より高速でテストが容易です。

// DO: Static pure function
public static class OrderCalculator
{
    public static Money CalculateTotal(IReadOnlyList<OrderItem> items)
    {
        var total = items.Sum(i => i.Price * i.Quantity);
        return new Money(total, "USD");
    }
}

// Usage - 予測可能、テスト可能
var total = OrderCalculator.CalculateTotal(items);

利点:

  • vtable ルックアップがない (より高速)
  • 隠れた状態がない
  • テストが容易 (純粋な入力 → 出力)
  • 設計上スレッドセーフ
  • 明示的な依存関係を強制する
// DON'T: 依存関係を隠すインスタンスメソッド
public class OrderCalculator
{
    private readonly ITaxService _taxService;  // 隠れた依存関係
    private readonly IDiscountService _discountService;  // 隠れた依存関係

    public Money CalculateTotal(IReadOnlyList<OrderItem> items)
    {
        // これは実際には何に依存しているのでしょうか?
    }
}

// BETTER: パラメータを介した明示的な依存関係
public static class OrderCalculator
{
    public static Money CalculateTotal(
        IReadOnlyList<OrderItem> items,
        decimal taxRate,
        decimal discountPercent)
    {
        // すべての入力が表示される
    }
}

やりすぎないでください - 状態またはポリモーフィズムが本当に必要な場合は、インスタンスメソッドを使用してください。


列挙を遅延する

列挙可能オブジェクトを、必要になるまで具体化しないでください。過剰な LINQ チェーンは避けてください。

// BAD: 時期尚早な具体化
public IReadOnlyList<Order> GetActiveOrders()
{
    return _orders
        .Where(o => o.IsActive)
        .ToList()  // 具体化された!
        .OrderBy(o => o.CreatedAt)  // 別のイテレーション
        .ToList();  // 再び具体化された!
}

// GOOD: 最後まで遅延する
public IReadOnlyList<Order> GetActiveOrders()
{
    return _orders
        .Where(o => o.IsActive)
        .OrderBy(o => o.CreatedAt)
        .ToList();  // 単一の具体化
}

// GOOD: 呼び出し元がすべてのアイテムを必要としない可能性がある場合は、IEnumerable を返す
public IEnumerable<Order> GetActiveOrders()
{
    return _orders
        .Where(o => o.IsActive)
        .OrderBy(o => o.CreatedAt);
    // 呼び出し元がいつ具体化するかを決定する
}

Async Enumeration

async と IEnumerable には注意してください。

// BAD: LINQ での Async - 隠れた割り当て
var results = orders
    .Select(async o => await ProcessOrderAsync(o))  // アイテムごとに Task!
    .ToList();
await Task.WhenAll(results);

// GOOD: ストリーミングには IAsyncEnumerable を使用する
public async IAsyncEnumerable<OrderResult> ProcessOrdersAsync(
    IEnumerable<Order> orders,
    [EnumeratorCancellation] CancellationToken ct = default)
{
    foreach (var order in orders)
    {
        ct.ThrowIfCancellationRequested();
        yield return await ProcessOrderAsync(order, ct);
    }
}

// GOOD: 並列処理のためのバッチ処理
var results = await Task.WhenAll(
    orders.Select(o => ProcessOrderAsync(o)));

ValueTask vs Task

頻繁に同期的に完了するホットパスには ValueTask を使用します。実際の I/O には、Task を使用してください。

// DO: キャッシュされた/同期パスのための ValueTask
public ValueTask<User?> GetUserAsync(UserId id)
{
    if (_cache.TryGetValue(id, out var user))
    {
        return ValueTask.FromResult<User?>(user);  // 割り当てなし
    }

    return new ValueTask<User?>(FetchUserAsync(id));
}

// DO: 実際の I/O のための Task (よりシンプル、落とし穴がない)
public Task<Order> CreateOrderAsync(CreateOrderCommand cmd)
{
    // これは常にデータベースにアクセスします
    return _repository.CreateAsync(cmd);
}

ValueTask のルール:

  • ValueTask を複数回 await しないでください
  • .Result または `.GetAwaite を絶対に使用しないでください
📜 原文 SKILL.md(Claudeが読む英語/中国語)を展開

Type Design for Performance

When to Use This Skill

Use this skill when:

  • Designing new types and APIs
  • Reviewing code for performance issues
  • Choosing between class, struct, and record
  • Working with collections and enumerables

Core Principles

  1. Seal your types - Unless explicitly designed for inheritance
  2. Prefer readonly structs - For small, immutable value types
  3. Prefer static pure functions - Better performance and testability
  4. Defer enumeration - Don't materialize until you need to
  5. Return immutable collections - From API boundaries

Seal Classes by Default

Sealing classes enables JIT devirtualization and communicates API intent.

// DO: Seal classes not designed for inheritance
public sealed class OrderProcessor
{
    public void Process(Order order) { }
}

// DO: Seal records (they're classes)
public sealed record OrderCreated(OrderId Id, CustomerId CustomerId);

// DON'T: Leave unsealed without reason
public class OrderProcessor  // Can be subclassed - intentional?
{
    public virtual void Process(Order order) { }  // Virtual = slower
}

Benefits:

  • JIT can devirtualize method calls
  • Communicates "this is not an extension point"
  • Prevents accidental breaking changes

Readonly Structs for Value Types

Structs should be readonly when immutable. This prevents defensive copies.

// DO: Readonly struct for immutable value types
public readonly record struct OrderId(Guid Value)
{
    public static OrderId New() => new(Guid.NewGuid());
    public override string ToString() => Value.ToString();
}

// DO: Readonly struct for small, short-lived data
public readonly struct Money
{
    public decimal Amount { get; }
    public string Currency { get; }

    public Money(decimal amount, string currency)
    {
        Amount = amount;
        Currency = currency;
    }
}

// DON'T: Mutable struct (causes defensive copies)
public struct Point  // Not readonly!
{
    public int X { get; set; }  // Mutable!
    public int Y { get; set; }
}

When to Use Structs

Use Struct When Use Class When
Small (≤16 bytes typically) Larger objects
Short-lived Long-lived
Frequently allocated Shared references needed
Value semantics required Identity semantics required
Immutable Mutable state

Prefer Static Pure Functions

Static methods with no side effects are faster and more testable.

// DO: Static pure function
public static class OrderCalculator
{
    public static Money CalculateTotal(IReadOnlyList<OrderItem> items)
    {
        var total = items.Sum(i => i.Price * i.Quantity);
        return new Money(total, "USD");
    }
}

// Usage - predictable, testable
var total = OrderCalculator.CalculateTotal(items);

Benefits:

  • No vtable lookup (faster)
  • No hidden state
  • Easier to test (pure input → output)
  • Thread-safe by design
  • Forces explicit dependencies
// DON'T: Instance method hiding dependencies
public class OrderCalculator
{
    private readonly ITaxService _taxService;  // Hidden dependency
    private readonly IDiscountService _discountService;  // Hidden dependency

    public Money CalculateTotal(IReadOnlyList<OrderItem> items)
    {
        // What does this actually depend on?
    }
}

// BETTER: Explicit dependencies via parameters
public static class OrderCalculator
{
    public static Money CalculateTotal(
        IReadOnlyList<OrderItem> items,
        decimal taxRate,
        decimal discountPercent)
    {
        // All inputs visible
    }
}

Don't go overboard - Use instance methods when you genuinely need state or polymorphism.


Defer Enumeration

Don't materialize enumerables until necessary. Avoid excessive LINQ chains.

// BAD: Premature materialization
public IReadOnlyList<Order> GetActiveOrders()
{
    return _orders
        .Where(o => o.IsActive)
        .ToList()  // Materialized!
        .OrderBy(o => o.CreatedAt)  // Another iteration
        .ToList();  // Materialized again!
}

// GOOD: Defer until the end
public IReadOnlyList<Order> GetActiveOrders()
{
    return _orders
        .Where(o => o.IsActive)
        .OrderBy(o => o.CreatedAt)
        .ToList();  // Single materialization
}

// GOOD: Return IEnumerable if caller might not need all items
public IEnumerable<Order> GetActiveOrders()
{
    return _orders
        .Where(o => o.IsActive)
        .OrderBy(o => o.CreatedAt);
    // Caller decides when to materialize
}

Async Enumeration

Be careful with async and IEnumerable:

// BAD: Async in LINQ - hidden allocations
var results = orders
    .Select(async o => await ProcessOrderAsync(o))  // Task per item!
    .ToList();
await Task.WhenAll(results);

// GOOD: Use IAsyncEnumerable for streaming
public async IAsyncEnumerable<OrderResult> ProcessOrdersAsync(
    IEnumerable<Order> orders,
    [EnumeratorCancellation] CancellationToken ct = default)
{
    foreach (var order in orders)
    {
        ct.ThrowIfCancellationRequested();
        yield return await ProcessOrderAsync(order, ct);
    }
}

// GOOD: Batch processing for parallelism
var results = await Task.WhenAll(
    orders.Select(o => ProcessOrderAsync(o)));

ValueTask vs Task

Use ValueTask for hot paths that often complete synchronously. For real I/O, just use Task.

// DO: ValueTask for cached/synchronous paths
public ValueTask<User?> GetUserAsync(UserId id)
{
    if (_cache.TryGetValue(id, out var user))
    {
        return ValueTask.FromResult<User?>(user);  // No allocation
    }

    return new ValueTask<User?>(FetchUserAsync(id));
}

// DO: Task for real I/O (simpler, no footguns)
public Task<Order> CreateOrderAsync(CreateOrderCommand cmd)
{
    // This always hits the database
    return _repository.CreateAsync(cmd);
}

ValueTask rules:

  • Never await a ValueTask more than once
  • Never use .Result or .GetAwaiter().GetResult() before completion
  • If in doubt, use Task

Span and Memory for Bytes

Use Span<T> and Memory<T> instead of byte[] for low-level operations.

// DO: Accept Span for synchronous operations
public static int ParseInt(ReadOnlySpan<char> text)
{
    return int.Parse(text);
}

// DO: Accept Memory for async operations
public async Task WriteAsync(ReadOnlyMemory<byte> data)
{
    await _stream.WriteAsync(data);
}

// DON'T: Force array allocation
public static int ParseInt(string text)  // String allocated
{
    return int.Parse(text);
}

Common Span Patterns

// Slice without allocation
ReadOnlySpan<char> span = "Hello, World!".AsSpan();
var hello = span[..5];  // No allocation

// Stack allocation for small buffers
Span<byte> buffer = stackalloc byte[256];

// Use ArrayPool for larger buffers
var buffer = ArrayPool<byte>.Shared.Rent(4096);
try
{
    // Use buffer...
}
finally
{
    ArrayPool<byte>.Shared.Return(buffer);
}

Collection Return Types

Return Immutable Collections from APIs

// DO: Return immutable collection
public IReadOnlyList<Order> GetOrders()
{
    return _orders.ToList();  // Caller can't modify internal state
}

// DO: Use frozen collections for static data (.NET 8+)
private static readonly FrozenDictionary<string, Handler> _handlers =
    new Dictionary<string, Handler>
    {
        ["create"] = new CreateHandler(),
        ["update"] = new UpdateHandler(),
    }.ToFrozenDictionary();

// DON'T: Return mutable collection
public List<Order> GetOrders()
{
    return _orders;  // Caller can modify!
}

Internal Mutation is Fine

public IReadOnlyList<OrderItem> BuildOrderItems(Cart cart)
{
    var items = new List<OrderItem>();  // Mutable internally

    foreach (var cartItem in cart.Items)
    {
        items.Add(CreateOrderItem(cartItem));
    }

    return items;  // Return as IReadOnlyList
}

Collection Guidelines

Scenario Return Type
API boundary IReadOnlyList<T>, IReadOnlyCollection<T>
Static lookup data FrozenDictionary<K,V>, FrozenSet<T>
Internal building List<T>, then return as readonly
Single item or none T? (nullable)
Zero or more, lazy IEnumerable<T>

Quick Reference

Pattern Benefit
sealed class Devirtualization, clear API
readonly record struct No defensive copies, value semantics
Static pure functions No vtable, testable, thread-safe
Defer .ToList() Single materialization
ValueTask for hot paths Avoid Task allocation
Span<T> for bytes Stack allocation, no copying
IReadOnlyList<T> return Immutable API contract
FrozenDictionary Fastest lookup for static data

Anti-Patterns

// DON'T: Unsealed class without reason
public class OrderService { }  // Seal it!

// DON'T: Mutable struct
public struct Point { public int X; public int Y; }  // Make readonly

// DON'T: Instance method that could be static
public int Add(int a, int b) => a + b;  // Make static

// DON'T: Multiple ToList() calls
items.Where(...).ToList().OrderBy(...).ToList();  // One ToList at end

// DON'T: Return List<T> from public API
public List<Order> GetOrders();  // Return IReadOnlyList<T>

// DON'T: ValueTask for always-async operations
public ValueTask<Order> CreateOrderAsync();  // Just use Task

Resources