C#を用いたアプリケーション開発において、特定のデータが文字列やリストのどこに存在するかを特定する作業は非常に頻繁に発生します。

その際、最も基本的かつ強力なツールとなるのが IndexOfメソッド です。

このメソッドは、指定した要素が最初に出現する位置をインデックス番号で返すもので、文字列操作からデータ構造の検索まで幅広いシーンで活用されます。

本記事では、C#の IndexOf メソッドについて、文字列(String)、リスト(List)、配列(Array)での基本的な使い方から、実戦で役立つ応用テクニック、パフォーマンス上の注意点までを詳しく解説します。

最新の .NET 環境においても標準的に使用されるこの機能をマスターし、より効率的で堅牢なコードを書けるようになりましょう。

IndexOfメソッドの基本概念

IndexOf メソッドの役割は、対象となるシーケンス(文字列や配列など)の中から 指定された値と一致する最初の要素のインデックスを特定すること です。

C#のインデックスは 0から始まる「0オリジン」 であるため、先頭で見つかった場合は 0 が返されます。

もし指定した値が見つからなかった場合には、-1 が返されるという点が最大の特徴です。

この戻り値の仕様を理解しておくことで、「値が存在するかどうか」という条件分岐(if文)と「どの位置にあるか」という位置情報の取得を同時に行うことができます。

文字列検索における IndexOf (string.IndexOf)

文字列操作はプログラム開発において最も一般的なタスクの一つです。

string.IndexOf を使用することで、特定の文字や部分文字列がどこに含まれているかを簡単に調べることができます。

基本的な使い方

最もシンプルな使い方は、検索対象の文字列に対して引数として文字(char)または文字列(string)を渡す方法です。

C#
using System;

public class Program
{
    public static void Main()
    {
        string message = "Hello, C# World! Welcome to C# Programming.";
        
        // "C#" という部分文字列が最初に出現する位置を検索
        int index = message.IndexOf("C#");
        
        if (index != -1)
        {
            Console.WriteLine($"'C#' はインデックス {index} で最初に見つかりました。");
        }
        else
        {
            Console.WriteLine("'C#' は見つかりませんでした。");
        }
    }
}
実行結果
'C#' はインデックス 7 で最初に見つかりました。

検索開始位置と検索範囲の指定

IndexOf には、検索を開始する位置を指定するオーバーロードが存在します。

これにより、2番目以降の出現位置を探すといった処理が可能になります。

C#
using System;

public class Program
{
    public static void Main()
    {
        string text = "apple, banana, cherry, apple, date";
        
        // 最初の "apple" を検索
        int firstIndex = text.IndexOf("apple");
        
        // 最初の位置の次から検索を開始して、2番目の "apple" を探す
        int secondIndex = text.IndexOf("apple", firstIndex + 1);
        
        Console.WriteLine($"1番目の位置: {firstIndex}");
        Console.WriteLine($"2番目の位置: {secondIndex}");
    }
}
実行結果
1番目の位置: 0
2番目の位置: 23

大文字・小文字を区別しない検索 (StringComparison)

デフォルトの IndexOf は、大文字と小文字を厳密に区別します。

しかし、実務ではユーザー入力の検索などで「大文字・小文字を無視したい」ケースが多いでしょう。

その場合は、StringComparison 列挙型 を引数に指定します。

StringComparison.OrdinalIgnoreCase を使用するのが、パフォーマンスと正確性のバランスが取れた一般的な手法です。

C#
using System;

public class Program
{
    public static void Main()
    {
        string title = "Learning C# Programming";
        
        // 大文字小文字を無視して "c#" を検索
        int index = title.IndexOf("c#", StringComparison.OrdinalIgnoreCase);
        
        if (index != -1)
        {
            Console.WriteLine($"見つかった位置: {index}");
        }
    }
}
実行結果
見つかった位置: 9
指定値説明
Ordinalバイナリ値に基づいて比較(デフォルト)。高速。
OrdinalIgnoreCaseバイナリ値に基づいて大文字小文字を無視して比較。推奨される設定。
CurrentCulture現在の言語設定(カルチャ)に基づいて比較。
InvariantCulture言語に依存しない不変のカルチャで比較。

リストにおける IndexOf (List<T>.IndexOf)

動的な配列である List<T> クラスでも、IndexOf メソッドは非常によく使われます。

リスト内の特定のオブジェクトが何番目にあるかを知ることで、要素の削除や置換などの操作を正確に行うことができます。

基本的な検索

数値や文字列のリストであれば、直感的に検索が可能です。

C#
using System;
using System.Collections.Generic;

public class Program
{
    public static void Main()
    {
        List<string> fruits = new List<string> { "Apple", "Banana", "Orange", "Grape" };
        
        int index = fruits.IndexOf("Orange");
        
        if (index >= 0)
        {
            Console.WriteLine($"Orange はリストの {index} 番目にあります。");
        }
    }
}
実行結果
Orange はリストの 2 番目にあります。

参照型オブジェクトの検索と Equals の関係

List<T>.IndexOf は内部的に要素の比較を行いますが、クラスなどの参照型の場合、デフォルトではインスタンスの参照先が同じかどうか で判断されます。

内容が同じ別インスタンスを探したい場合は、Equals メソッドをオーバーライドするか、LINQ の FindIndex を検討する必要があります。

C#
using System;
using System.Collections.Generic;

public class User
{
    public int Id { get; set; }
    public string Name { get; set; }
}

public class Program
{
    public static void Main()
    {
        User u1 = new User { Id = 1, Name = "Alice" };
        User u2 = new User { Id = 2, Name = "Bob" };
        List<User> users = new List<User> { u1, u2 };
        
        // 同じプロパティ値を持つが、インスタンスが異なる場合
        User target = new User { Id = 1, Name = "Alice" };
        
        int index = users.IndexOf(target);
        
        Console.WriteLine($"検索結果(参照が異なる場合): {index}"); // -1 が返る
        
        // インスタンスそのものを渡した場合
        int indexCorrect = users.IndexOf(u1);
        Console.WriteLine($"検索結果(同じインスタンスの場合): {indexCorrect}"); // 0 が返る
    }
}
実行結果
検索結果(参照が異なる場合): -1
検索結果(同じインスタンスの場合): 0

配列における IndexOf (Array.IndexOf)

配列(Array)の場合、インスタンスメソッドではなく、System.Array クラスの静的メソッド として IndexOf が提供されています。

使い方がリストや文字列とは若干異なるため注意が必要です。

Array.IndexOf の構文

第一引数に検索対象の配列、第二引数に検索したい値を指定します。

C#
using System;

public class Program
{
    public static void Main()
    {
        int[] numbers = { 10, 20, 30, 40, 50 };
        
        // 配列から 30 を検索
        int index = Array.IndexOf(numbers, 30);
        
        if (index != -1)
        {
            Console.WriteLine($"値 30 はインデックス {index} にあります。");
        }
    }
}
実行結果
値 30 はインデックス 2 にあります。

IndexOf と類似メソッドの使い分け

C#には要素を検索するためのメソッドが複数存在します。

目的や状況に応じて、IndexOf と他のメソッドを適切に使い分けることが重要です。

Contains との比較

  • IndexOf: 見つかった 位置(インデックス) が必要な場合に使用します。
  • Contains: 存在するかどうかの 真偽値(bool) だけが必要な場合に使用します。内部的には IndexOf と似た処理が行われますが、意図が明確になります。

LastIndexOf

IndexOf は先頭から検索しますが、LastIndexOf末尾から逆方向に検索 します。

ファイルパスから拡張子を抽出するためにドット(.)の最後の位置を探す際などに重宝します。

FindIndex (LINQ / List)

IndexOf は「特定の値」を検索しますが、FindIndex は「特定の条件(ラムダ式)」に合致する最初の要素を検索します。

先ほどの「プロパティの値が一致するクラスの要素を探す」といったケースでは、FindIndex が最適です。

C#
// IDが2のユーザーのインデックスを探す
int index = users.FindIndex(u => u.Id == 2);

パフォーマンスと注意点

計算量について

IndexOf は基本的に O(n) の計算量となります。

これは、要素を先頭から一つずつ順番にチェックするためです。

リストや配列の要素数が非常に多い(数百万件など)場合、頻繁に IndexOf を呼び出すとパフォーマンス低下の原因になります。

大量のデータを高速に検索する必要がある場合は、Dictionary<TKey, TValue>HashSet<T> などのハッシュテーブルを利用したデータ構造への変更を検討してください。

これらは O(1) に近い速度で要素の有無を確認できます。

Span<T> への対応

最新の .NET(C# 7.2以降)では、メモリ効率を最大化するために ReadOnlySpan<char>Span<T> を使用することが増えています。

これらの型にも IndexOf 拡張メソッドが提供されており、文字列の一部を切り出す(スライスする)際などに 新しい文字列インスタンスを生成せずに高速に検索 することが可能です。

C#
// Span を使った効率的な検索の例
ReadOnlySpan<char> span = "Optimization is key".AsSpan();
int spaceIndex = span.IndexOf(' ');

実践的な活用事例

ここでは、実際の開発現場で IndexOf がどのように活用されるかの例を紹介します。

特定の文字に囲まれた文字列の抽出

ログ解析やデータパースにおいて、特定の記号で囲まれた部分を取り出したい場合があります。

C#
using System;

public class Program
{
    public static void Main()
    {
        string log = "ERROR [2026-02-21]: Connection failed.";
        
        // ブラケット [ ] の位置を探す
        int start = log.IndexOf("[");
        int end = log.IndexOf("]");
        
        if (start != -1 && end != -1 && end > start)
        {
            // 中身を抽出
            string datePart = log.Substring(start + 1, end - start - 1);
            Console.WriteLine($"抽出された日付: {datePart}");
        }
    }
}
実行結果
抽出された日付: 2026-02-21

特定のキーワードがすべて出現する位置をリストアップ

ループと startIndex 指定を組み合わせることで、文字列内にあるすべての出現箇所を取得できます。

C#
using System;
using System.Collections.Generic;

public class Program
{
    public static void Main()
    {
        string content = "C# is powerful. C# is versatile. C# is fun.";
        string keyword = "C#";
        List<int> foundIndices = new List<int>();
        
        int index = content.IndexOf(keyword);
        while (index != -1)
        {
            foundIndices.Add(index);
            // 次の検索は現在見つかった位置の次から開始
            index = content.IndexOf(keyword, index + keyword.Length);
        }
        
        Console.WriteLine($"'{keyword}' の出現位置: {string.Join(", ", foundIndices)}");
    }
}
実行結果
'C#' の出現位置: 0, 16, 33

まとめ

C#の IndexOfメソッド は、データの位置を特定するための最も基本的かつ重要なメソッドです。

文字列、リスト、配列のいずれにおいても一貫した考え方で使用でき、戻り値が -1 であるかどうかを確認するだけで、存在チェックと位置取得を同時に行える利便性があります。

本記事で解説した以下のポイントを意識することで、より高度な実装が可能になります。

  • StringComparison.OrdinalIgnoreCase を活用して、大文字小文字を柔軟に扱う。
  • 2番目以降の要素を探す際は、開始インデックス を指定する。
  • 静的な Array.IndexOf とインスタンスメソッドの List.IndexOf の違いを理解する。
  • 複雑な条件や参照型の内容比較が必要な場合は FindIndex も検討する。

これらのテクニックを駆使して、効率的で読みやすいC#プログラミングを実践していきましょう。