泥庭

2016年10月27日

[C#読書会]TryGetでtryブロック追放

Filed under: .NET, C#, 読書会 — yone64 @ 12:39 午前
久々です。
最近、C#読書会なるもので、「C#ショートコードプログラミング」を読んでいます。
ので、軽く感想とか((続くかなぁ?

で、今回は「4.3 TryGetでtryブロック追放」からでした
Dictionaryクラスはキーがない場合は例外が発生するので、TryGetValueを使いましょうね。という話です。特に異議なし。
# そもそもなんでこんなテーマ?と思い巡らせていたら、
# その昔、C#にGenericがなかったころの連想配列であるHashTableは
# キーがない場合にnullを返してたということを思い出したw。
http://www.atmarkit.co.jp/fdotnet/csharp20/csharp20_02/csharp20_02_03.html

TryGetValueの使い方的には下記通り。ContainsKeyした後に値を取得するのと同等の処理が1メソッドで実行可能です。
var dic = new Dictionary<int, string>();
string result;
if (dic.TryGetValue(2, out result))
{
    // dictionaryに含まれていた場合の処理。
}

TryGetValueには、個人的に気に入らない点があって、out用の変数を前もって宣言しないといけないところです。このせいで、ラムダ式がExpression-Body形式でかけなかったりと割と不便なんですよね。
まぁ、この点はC#7で改善されるらしいので期待。
# ところで、変数のスコープが広くなるのって誰がうれしいんだろう?って思ったりしてます。

というわけで、現状はTryGetValueを使うことになるのですが、あまり便利じゃないので拡張メソッドで各種便利にしようって試み。

その① あったら取得、なかったら追加して取得。

public static TValue GetOrAdd<TKey, TValue>(this Dictionary<TKey, TValue> source, TKey key, Func<TKey, TValue> func)
{
    TValue result;
    if (!source.TryGetValue(key, out result))
    {
        result = func(key);
        source[key] = result;
    }
    return result;
}
割とよくあるシチュエーションですので、こういう拡張メソッドがあれば便利かもですね。でも、最近はスレッドセーフなConcurrentDictionaryを使う一択な気もします。最初からGetOrAddメソッドが用意されています。

その② なかったらデフォルト値を返却。

public static TValue GetValueOrDefalut<TKey, TValue>(this Dictionary<TKey, TValue> source, TKey key, TValue defaultValue = default(TValue))
{
    TValue result;
    return source.TryGetValue(key, out result) ? result : defaultValue;
}
public static TValue GetValueOrDefalut<TKey, TValue>(this Dictionary<TKey, TValue> source, TKey key, Func<TKey, TValue> func)
{
    TValue result;
    return source.TryGetValue(key, out result) ? result : func(key);
}
これも割とありがちですね。即値を返すメソッドとデリゲートを引数に取るメソッドの2つ用意しておくと便利です。

その③ 値があったら実行

public static void GetValueAndDo<TKey, TValue>(this Dictionary<TKey, TValue> source, TKey key, Action<TValue> action)
{
    TValue result;
    if (source.TryGetValue(key, out result))
    {
        action(result);
    }
}
②とほぼ同じ。戻値を使って何かしたいケースですね。
大体これぐらいあれば、何とかなるんじゃないでしょうか?((適当

話は変わって、Dictionary系の拡張メソッドを作る場合の注意点というか面倒くさい点があってですね。
// IDictionary型に対して拡張メソッドを定義する
public static void DicExt<TKey, TValue>(this IDictionary<TKey, TValue> source);
// IReadOnlyDictionary型はIDictionary型と継承関係がないので別途定義が必要
public static void DicExt<TKey, TValue>(this IReadOnlyDictionary<TKey, TValue> source);
// DictionaryとConcurrentDictionaryは、上記2つのどちらのインターフェースも実装しているので
// 使用する拡張メソッドが解決しないため、個別に拡張メソッドが必要になる
public static void DicExt<TKey, TValue>(this Dictionary<TKey, TValue> source);
public static void DicExt<TKey, TValue>(this ConcurrentDictionary<TKey, TValue> source);
と、同じ中身のメソッドを山程定義せにゃならんのですよ。なんかいい解決方法ないですかね?

コメントする »

まだコメントはありません。

RSS feed for comments on this post. TrackBack URI

コメントを残す

WordPress.com で無料サイトやブログを作成.