LINQクエリ結果へのインデックスアクセス
C#で配列やList<T>を扱う場合、array[0]のようにインデクサ([])を使って特定の位置にある要素にアクセスするのが一般的です。
しかし、LINQのメソッド(OrderByやWhereなど)が返す結果はIEnumerable<T>型であり、この型はインデクサ([])をサポートしていません。そのため、クエリ結果をToArray()などで配列に変換せずに、直接「N番目の要素」を取得したい場合には、LINQのElementAtメソッドを使用します。
この記事では、ElementAtメソッドの使い方と、範囲外アクセス時の例外を回避するElementAtOrDefaultメソッドについて解説します。
ElementAt(int index) メソッド
ElementAtメソッドは、シーケンス内の指定されたインデックス(0から始まる)にある要素を返します。
コード例:ソート後の特定順位を取得
整数のリストを昇順に並べ替え(ソート)、その中から「3番目に小さい値(インデックス 2)」を取得する例です。
using System;
using System.Collections.Generic;
using System.Linq;
public class ElementAtExample
{
public static void Main()
{
// ランダムな数値リスト
var scores = new List<int> { 85, 42, 99, 60, 73, 100 };
// 1. スコアを昇順(小さい順)に並べ替え
// 2. ElementAt(2) で「3番目」の要素を取得
// (ソート後: 42, 60, 73, 85, 99, 100)
int thirdLowestScore = scores.OrderBy(n => n).ElementAt(2);
Console.WriteLine($"ソート前のリスト: {string.Join(", ", scores)}");
Console.WriteLine($"3番目に小さいスコア: {thirdLowestScore}");
}
}
出力結果:
ソート前のリスト: 85, 42, 99, 60, 73, 100
3番目に小さいスコア: 73
注意点:ArgumentOutOfRangeException
ElementAtメソッドは、指定したインデックスが負の数であったり、要素数以上であったりした場合、ArgumentOutOfRangeException という例外をスローしてプログラムを停止させます。
要素数が不確定な場合は、事前に.Count()で要素数を確認するか、次項のElementAtOrDefaultを使用する必要があります。
ElementAtOrDefault(int index) メソッド
ElementAtOrDefaultメソッドは、指定したインデックスが範囲外であった場合に例外をスローせず、その型の**既定値(デフォルト値)**を返します。
- 参照型(
stringなど):null - 数値型(
intなど):0
コード例:範囲外アクセスの安全な処理
商品リストから特定の要素を取得しようとし、存在しない場合でも安全に処理を続行する例です。
using System;
using System.Collections.Generic;
using System.Linq;
public class ElementAtOrDefaultExample
{
public static void Main()
{
var products = new List<string> { "Keyboard", "Mouse", "Monitor" };
// インデックス 10 は存在しない(範囲外)
// ElementAt なら例外が発生するが、ElementAtOrDefault なら null が返る
string? item = products.ElementAtOrDefault(10);
if (item != null)
{
Console.WriteLine($"取得した商品: {item}");
}
else
{
Console.WriteLine("指定された位置に商品は存在しません。");
}
// null合体演算子 (??) との組み合わせが便利
string displayItem = products.ElementAtOrDefault(5) ?? "(該当なし)";
Console.WriteLine($"表示: {displayItem}");
}
}
出力結果:
指定された位置に商品は存在しません。
表示: (該当なし)
パフォーマンスに関する考慮
ElementAtメソッドは、対象のコレクションがList<T>や配列のように「インデックスによるランダムアクセス」をサポートしている場合、最適化されて高速に動作します。
しかし、Whereメソッドの結果など、インデックスを持たないシーケンス(IEnumerable<T>)に対して使用した場合、先頭から指定された位置まで順番に要素をたどる(カウントする)処理が発生します。
そのため、巨大なシーケンスに対して大きなインデックスを指定してElementAtを繰り返し呼び出すと、処理速度が低下する可能性があります。
まとめ
ElementAt(index): 指定位置の要素を取得します。範囲外の場合は例外が発生します。要素が必ず存在すると確信できる場合に使用します。ElementAtOrDefault(index): 指定位置の要素を取得します。範囲外の場合は**既定値(nullなど)**が返されます。要素数が不明確な場合や、安全性を優先する場合に使用します。
配列の [] と同様の感覚で使えますが、LINQクエリの結果(IEnumerable<T>)に対して直接適用できる点が大きなメリットです。
