Edited at
gloopsDay 19

【C#】あるとちょっと便利な拡張メソッド紹介

More than 1 year has passed since last update.


gloops advent calenda 19日目

gloops advent calendar 19日目です:santa_tone2:

はじめまして。 gloopsの s_mino_ri です。

社内ではゲームの運用に携わっているとともに、常にC#の情報を追いかけている生粋のC#オタクでもあります。


あるとちょっと便利な拡張メソッド

ここでは、私が運用に携わっているゲームで使用している、独自定義された拡張メソッドを紹介していきたいと思います。

※読みやすさを優先して、引数のnullチェックや例外処理は基本的に省いています

※以下で紹介するメソッドは、すべて現場で使われているままの名前です


そもそも拡張メソッドとは?

拡張メソッド (C# プログラミング ガイド)

拡張メソッドとは、C#に用意された言語機能のひとつで、あたかも「既存の型(クラスなど)にメソッドを追加したように見える」メソッドです。

実態はクラスの外にあるただのstaticなメソッドので、クラス内のprivateな変数などにはアクセスできません。

有名なところではLINQが主に拡張メソッドで実装されていますね。

当然、拡張メソッドを使いすぎるとメソッドがどこで定義されているのか分からなくなるなどデメリットもあるため、乱用は禁物です。


ページ分け表示処理


Pagenate

public static IEnumerable<T> Paginate<T>(this IEnumerable<T> source, int pageSize, int currentPage)

{
return source.Skip(pageSize * (currentPage - 1)).Take(pageSize);
}

大量にあるデータを5件ずつなどで区切ってページ分け表示する際に使用します。

単純ですが、ページ分けする場所それぞれにSkipとTakeを書くよりも一貫性が出て読みやすくなります。


確率算出


CalcProbability

public static double CalcProbability<T>(this IEnumerable<T> source, Func<T, int> rateSelector, Func<T, bool> predicate)

{
var denominator = source.Sum(rateSelector);
var numerator = source.Where(predicate).Sum(rateSelector);
return numerator / (double)denominator;
}

ソーシャルゲームにガチャはつきもの。

このメソッドは、ある集団の中で、特定の集団が選択される確率を計算します。

rateSelectorは要素(ガチャの排出アイテムなど)から、その要素に割り当てられた排出の重み値を選択する述語です。


小分けに分割


Chunk

public static IEnumerable<IList<T>> Chunk<T>(this IEnumerable<T> source, int chunkSize)

{
var result = new List<T>(chunkSize);

foreach (var item in source)
{
result.Add(item);
if (result.Count < chunkSize) continue;
yield return result;
result = new List<T>(chunkSize);
}

if (result.Any())
yield return result;
}


RxにあるBufferとほぼ同じ動作をする拡張メソッドです。大量のデータを、ある個数ごとに分割して返します。

たとえば、所持アイテムを5個ごとに折り返して表示する場合などに使用します。


LINQの流れで文字列にする


StringJoin

public static string StringJoin<T>(this IEnumerable<T> source, string separator)

{
return string.Join(separator, source);
}

LINQでシーケンスを加工した後、そのままstring.Joinしたい時に頭から囲うのは面倒……。

という要件を叶えるだけのメソッドです。


Dictionaryに絶妙に足りないメソッド


GetValueOrDefault

public static TValue GetValueOrDefault<TKey, TValue>(this IDictionary<TKey, TValue> dictionary, TKey key)

{
TValue result;
dictionary.TryGetValue(key, out result); // keyが見つからない場合は result に default(TValue) が代入される
return result;
}

標準ライブラリのDictionaryは、「特定のキーが見つからない場合に規定値を返す」という要件を満たすメソッドがありません。

TryGetをこのために使うと、毎回変数を先に宣言しないといけないため少し不便…ということで用意したメソッドです。


SQLのBETWEEN...AND...を移植


InBetween

public static bool InBetween<T>(this T value, T min, T max)

where T : IComparable<T>
{
return value.CompareTo(min) >= 0 && value.CompareTo(max) <= 0;
}

ある値が、下限~上限の間に入っているか判定する拡張メソッドです。

不等号の単純なミスを無くしてくれる上、コード上の値の並び順が分かりやすくなります。


まとめ

拡張メソッドは、うまく使うとコードの可読性を上げ、一連の処理を再利用しやすくします。

しかし、前述したようにデメリットもあり、「パッと見で誰が定義したメソッドか分かりにくくなる」「ワンライナー化が(過剰に)進み、コードが横に伸びやすくなる」などのかえって可読性を低下させるような効果をもたらすこともあります。

便利な機能ですが、使いすぎには気を付けましょう。