【C#】配列 や List の TryGetValue 
2021/05/17 Mon [edit]
今回はとても簡単なもので、意外と便利なもの。
配列や List を扱う上で注意すべき点は、インデクス範囲外(Index was out of range) と null かどうかだが、毎回 null チェックや範囲外チェックをやるのは少し面倒である。
Dictionary ではキーを用いて値の取得を試みる TryGetValue があるが、これと同じ様なものがあったら便利なのにな~、と思ったので作ってみた。
●配列, List の TryGetValue
using System;
using System.Collections.Generic;
public static class Extensions //名前は任意
{
/// <summary>
/// 配列 の TryGetValue
/// ・配列自体が null or インデクスが範囲外のとき false
/// 2021/05/17 Fantom
/// http://fantom1x.blog130.fc2.com/blog-entry-397.html
/// </summary>
/// <typeparam name="T">要素の型</typeparam>
/// <param name="array">対象の配列</param>
/// <param name="idx">取得するインデクス</param>
/// <param name="value">取得成功したときの値</param>
/// <returns>true = 取得成功</returns>
public static bool TryGetValue<T>(this T[] array, int idx, out T value)
{
value = default(T);
if (array == null || idx < 0 || array.Length <= idx)
return false;
value = array[idx];
return true;
}
/// <summary>
/// List の TryGetValue
/// ・List 自体が null or インデクスが範囲外のとき false
/// 2021/05/17 Fantom
/// http://fantom1x.blog130.fc2.com/blog-entry-397.html
/// </summary>
/// <typeparam name="T">要素の型</typeparam>
/// <param name="list">対象の List</param>
/// <param name="idx">取得するインデクス</param>
/// <param name="value">取得成功したときの値</param>
/// <returns>true = 取得成功</returns>
public static bool TryGetValue<T>(this List<T> list, int idx, out T value)
{
value = default(T);
if (list == null || idx < 0 || list.Count <= idx)
return false;
value = list[idx];
return true;
}
}
●メインコード例
using System;
using System.Collections.Generic;
using System.Linq;
int max = 50;
var array = Enumerable.Range(0, max).ToArray(); //0~49 まで
var list = Enumerable.Range(0, max).ToList(); //0~49 まで
int idxA = 25;
if (array.TryGetValue(idxA, out var resultA))
{
Console.WriteLine($"result value (Array) = {resultA}");
}
else
{
Console.WriteLine($"idx = {idxA} : Index was out of range or null (Array)");
}
int idxL = 50;
if (list.TryGetValue(idxL, out var resultL))
{
Console.WriteLine($"result value (List) = {resultL}");
}
else
{
Console.WriteLine($"idx = {idxL} : Index was out of range or null (List)");
}
//var res = list[100]; //Index was out of range
idx = 50 : Index was out of range or null (List)
本当になんてことないものなんだけどね。できれば標準関数に欲しいくらい(笑)。
例えば範囲外のインデクスのとき、予め Fallback 的な値を決めておく等、とても簡潔に書ける。
●例:範囲内なら配列の値を返し、範囲外なら 0 を返す
int[] arr = {0,1,2,3,4};
int GetOrFallback(int idx) => arr.TryGetValue(idx, out var res) ? res : 0;
ところで少し話はずれるが、Dictionary で値の取得を試みるとき、どうやっているだろうか?
例えば以下のような感じに書けると思うが、① と ② では実行速度が結構違う。
① ContainsKey を用いてから取得
if (dic.ContainsKey(key)) //dic は Dictionary とする
{
var res = dic[key];
//ここで res を使った処理
}
② TryGetValue で取得
if (dic.TryGetValue(key, out var res)) //dic は Dictionary とする
{
//ここで res を使った処理
}
① と ② は同じ結果にはなるが、実は実行速度は ① の方が約2倍かかる(遅い)。
理由は簡単で、① は ContainsKey で1度 get, インデクサ(dic[key]) でもう1度 get の計2回 get (取得)するからである。TryGetValue は1回で済むので、その分速い。
ほんのちょっとしたことでも、実行速度を改善できたりするので、特に高速なメインループ処理を実現したい時などに思い出してみると良い(Unity みたいな、毎フレーム回す処理には、徹底すれば意外と効果がある=アルゴリズム変更しない限りでは「手数を減らす」のが直接速度改善に繋がる)。
(関連記事)
【Unity】【C#】配列・リストのシャッフル
【C#】2次元配列(ジャグ配列・多次元配列)のソート
【C#】多次元配列とジャグ配列(2次元配列)のサイズ(長さ)、相互変換など
【C#】配列やListなどの中身(要素)を見る拡張メソッド Dump
- 関連記事
トラックバック
トラックバックURL
→http://fantom1x.blog130.fc2.com/tb.php/397-58fcca78
この記事にトラックバックする(FC2ブログユーザー)
| h o m e |