転職を繰り返したサラリーマンの多趣味ブログ

30才未経験でSEに転職した人の多趣味ブログ

【技術書メモ】C#プログラミングのイディオム / 定石&パターン③

ラムダ式

ラムダ式は一種のメソッドで、メソッドをかなり簡略化することができる。

public void Do() {

    var numbers = new[] { 5, 3, 9, 6, 7, 5, 8, 1, 0, 5, 10, 4 };

    // 変数judgeに処理内容を代入
    Predicate<int> judge = (int n) => {
        if (n % 2 == 0) {
            return true;
         } else {
             return false;
         }
     };

     // Countメソッドの呼び出し
     var count = Count(numbers, judge);
}

// 第2引数のjudgeには、int型を受け取り、bool値を返すメソッドを指定可
public int Count(int[] numbers, Predicate<int> judge) {
    int count = 0;
    foreach(var n in numbers) {
        if(judge(n) == true) {
            count++;
        }
    }

    return count;
}

上記のようなソースがあったとして、変数judgeに代入している処理は、ラムダ式でだいぶ簡略化できる。

  • returnの右側には、式を書ける。
  • ラムダ式の{}が1つであれば、{}とreturnを省略可。
  • 引数の型を明示しなくてもよい。
  • 引数が1つであれば、()を省略可。

省略したソースがこちら。

var count = Count(numbers, n => n % 2 == 0);

とかなり省略できるが、ラムダ式は全然慣れてないので、初見だと何をやっているかわからない。また、Predicateの理解も曖昧だし、メソッドの引数にメソッドを指定するって考え方もピンとこない。慣れたら、便利なのかな。

Listクラスとラムダ式の組み合わせ

Listクラスには、ラムダ式を引数に受け取れるメソッドがたくさんあるらしい。
まずは、以下Listがあるとする。

var list = new List<string> {
    "Tokyo", "New Delhi", "Bankok", "london", "Paris", "Berlin", "Canbera", "Hong Kong"
};

このListに対して、ラムダ式を使ったメソッド呼び出し例を記載していく。
Existsメソッド

// 引数で指定した条件に一致する要素がするかをtrue / falseで返却
// 最初の文字が'A'である要素がリスト内に存在するかチェック
var exists = list.Exists(s => s[0] == 'A');

Findメソッド

// 引数で指定した条件と一致する要素を検索し、最初に見つかった要素を返却
// 文字列の長さが6文字の最初の要素を返却する(Bankok)
var name = list.Find(s => s.Length == 6);

FindIndexメソッド

// 引数で指定した条件と一致する要素を検索し、インデックスを返却
// 要素の中からBerlinを検索し、そのインデックスを返却
int index = list.FindIndex(s => s == "Berlin");
Console.WriteLine(index);

FindAllメソッド

// 引数で指定した条件と一致する、すべての要素を取得する
// Tokyo, Parisを取得(文字列が5文字以下)
var names = list.FindAll(s => s.Length <= 5);
foreach (var s in names) {
    Console.WriteLine(s);
}

RemoveAllメソッド

// 引数で指定した条件と一致すよ要素をリストから削除し、戻り値は削除した要素数
// London, Hong Kongを削除
var removedCount = list.RemoveAll(s => s.Contains("on"));
Console.WriteLine(removedCount);

ForEachメソッド

// リストの各要素に対して引数で指定した処理を実行
list.ForEach(s => Console.WriteLine(s));

// 上と下は同じこと
foreach(var s in list) {
    Console.WriteLine(s);
}

ConvertAllメソッド

// リスト内の要素を別の型に変換し、変換された要素が格納されたリストを返却
var lowerList = list.ConvertAll(s => s.ToLower());
lowerList.ForEach(s => Console.WriteLine(s));

LINQ to Objectsの基礎

まず、LINQの概要としては、オブジェクト、データベース、XMLなどのさまざまなデータに対して標準化された方法で問い合わせできるというもの。自分の中では、ソースに対して実施できるSQLのイメージできる。(selectやwhereなどがあるから)
また、クエリ演算子はIEnumerableに対する拡張メソッドとして定義されている。
ここでクエリ演算子の例を一つ。

// Whereで5文字以下の要素に絞り、Selectで、引数にラムダ式で小文字変換を指定し、その結果を出力
IEnumerable<string> query = list.Where(s => s.Length <= 5).Select(s => s.ToLower());

※シーケンスとは
標準クエリ演算子の捜査対象のデータのこと。(配列やListなど)
IEnumerableインターフェイスを実装するオブジェクトはすべてシーケンスとして見なされ、LINQのクエリ演算子を使用し、さまざまな操作が可能になる。

遅延実行と即時実行

遅延実行とは、本当にデータが必要になったときにクエリが実行されて、LINQの大きな特徴の一つ。

var list = new List<string> {
    "Tokyo", "New Delhi", "Bankok", "london", "Paris", "Berlin", "Canbera", "Hong Kong"
};

// ここで変数queryに値を代入しているようにみえるが、実際は検索結果が代入されていない
var query = list.Where(s => s.Length <= 5);

list[0] = "Osaka";

// 値が実際に必要になったタイミングで、クエリ演算子が実行される。
// そのため、実行結果は"Osaka","Paris"
foreach(var i in query) {
    Console.WriteLine(i);
}

即時実行とは、クエリを明示的に実行させることをいう。ここでは、ToArrayメソッドを使用した例を示す。

var list = new List<string> {
    "Tokyo", "New Delhi", "Bankok", "london", "Paris", "Berlin", "Canbera", "Hong Kong"
};

// ここで、Whereメソッド実行
var query = list.Where(s => s.Length <= 5).ToArray();

list[0] = "Osaka";

// すでにWhereメソッドが実行されているため、実行結果は"Tokyo","Paris"
foreach(var i in query) {
    Console.WriteLine(i);
}