C#でlet, also

Kotlinのlet, also, run, apply

Kotlinで非常に便利な構文に、「let」「also」「run」「apply」があります。

これらはKotlinに標準で備わっているスコープ関数と呼ばれるもので、「対象となる変数や値に対する処理を行いつつ、その対象変数の値そのもの、または別の値を返す」関数です。

文章だとちょっとわかりづらいため、以下Kotlinでのサンプルコードで具体的な使用例を記載します。

サンプル1)letの例

letを使うことで、「値がnullを取りうる変数を、nullを許可しない引数が設定されている関数に渡したい」という処理がnullチェック無しに記述可能です。
また、その関数の結果をそのまま受け取ることもできます。

// letの実装例

// <>で囲まれたタグを除去する関数
fun removeHtmlTag(s: String): String {
    val regex = "<[^>]+>".toRegex()
    return regex.replace(s, "")
}


// HTMLタグに囲まれた中身を取り出し、タグ以外の文字数をカウントする関数
fun getHtmlContentCount(src: String?): Int {
    return src?.let { removeHtmlTag(it).length } ?: -1
}

// letを使わない例1
fun getHtmlContentCountWithoutLet01(src: String?): Int {
    return if (src != null) {
        removeHtmlTag(src).length
    } else {
        -1
    }
}

// letを使わない例2
fun getHtmlContentCountWithoutLet02(src: String?): Int {
    return when (src) {
        null -> -1
        else -> removeHtmlTag(src).length
    }
}

サンプル2)alsoの例

alsoでもletと同様にnullチェックの簡略化が可能ですが、letとは異なりalsoは自分自身を返すため、例えば「ある関数の戻り値を、一旦別の処理をはさんでそのまま返したい」という場合に便利です。
例えば以下のようにAndroidにてデータベース操作を行うケースです。

// alsoの例

// SQLiteDatabaseで処理を行いT型の結果を返す関数
fun <T> dbProcess(db: SQLiteDatabase, process: (SQLiteDatabase) -> T): T {
    return try {
        db.beginTransaction()
        process(db).also { db.setTransactionSuccessful() }
    } catch (ex: Exception) {
        throw ex
    } finally {
        db.endTransaction()
    }
}

// alsoを使わない例
fun <T> dbProcessWithoutAlso(db: SQLiteDatabase, process: (SQLiteDatabase) -> T): T {
    return try {
        db.beginTransaction()
        val result = process(db)    // 一時的な変数を設定
        db.setTransactionSuccessful()
        result
    } catch (ex: Exception) {
        throw ex
    } finally {
        db.endTransaction()
    }
}

上記のように、スコープ関するを用いることで、敢えて一時的な変数を宣言することなく値を処理して返すことができます。

また、letやalsoの関数内では参照する値の名前をデフォルトの「it」という名前から別名に変更することができます。

value.let { v ->
    // 別名指定により、valueはitではなくvとして扱われる
}

let・alsoは入れ子にして用いる場合も多く、デフォルトのitのままではどのlet・alsoの変数を指すのか大変わかりづらくなりますが、別名を指定することで大幅に混乱を防ぐことができます。

尚、スコープ関数はお互いによく似ていて区別がつきにくいため、以下の表にまとめます。

#スコープ関数名返す値変数名別名指定の可否
1let任意it
2run任意this
3also同じ値のみit
4apply同じ値のみthis
スコープ関数の比較表

C#で実装するには?

このletやalsoはコーディングする上で非常に便利なのですが、C#には標準で実装されていません。

そのため、Kotlinのletやalsoにあたる機能を、以下のようにC#で自前ライブラリに実装して使っています。

// C#でのlet・alsoの実装例
// 拡張として記載

using System;

namespace CS_Let_and_Also
{
    public static class CSLetAndAlso
    {
        public static R Let<T, R>(this T t, Func<T, R> func)
        {
            return func(t);
        }

		    public static T Also<T>(this T t, Action<T> action)
        {
			      action(t);
			      return t;
        }
    }
}

かなりシンプルですが、これだけです。

この拡張をライブラリ化しておけば、Kotlinでのletやalsoとほぼ同じ感覚で利用できます。

// C#でのlet・also使用例

using CS_Let_and_Also;

namespace CS_Let_and_Also
{

    public class Programs
    {
        public static void Main(params String[] args)
        {
            var str = "Hello World!";

            Console.WriteLine(str);

            // let
            Console.WriteLine(str.Let(s => s + ", LET!"));

            // also
            Console.WriteLine(str.Also(s => {
                Console.WriteLine("in the Also.");
            }));

            str = null;
            // nullチェック簡略化も可能
            Console.WriteLine(str?.Let(s => s + ", LET!") ?? "str is null");
        }
    }
}

/* 出力結果

Hello World!, LET!
in the Also.
Hello World!
str is null

*/

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です