今回はLINQ to Objectに絞って解説していきます。
環境 : Visual Studio 2015
2010とかでも問題なく使えたと思います。
LINQとは?
今回説明する LINQ to Object以外にもLINQ to XMLやLINQ to SQLなどがあります。
誤解を恐れずにざっくりまとめるとLINQ to XXXXとは
XXXXをデータベースのように操作・検索すること
じゃないかなと思います。
LINQ to Object の場合はコレクション(配列・リストなど)をデータベースのように操作・検索することです。
LINQの構文
クエリー式構文と
メソッド構文があります。
クエリー式構文
var select =
from testResult in testResults
let total = testResult.Mathematics + testResult.Physics + testResult.English
select new { testResult.Name, total };
クエリ風に記述可能です。他の言語の似たような機能と違ってsqlにかなり寄せてあるというのが特徴。
何かを継承する必要はなく同名のメソッドさえあればいいらしいです。
慣れてしまえば可読性は高いです。
メソッド構文
通常のメソッド呼び出しのように使えます。Count()やFirst()などもメソッド構文なので気付かずに使ってるパターンもあるかも?
メリット・デメリット
メリット
新しい表現が可能になる。構造化プログラミングともオブジェクト指向プログラミングとも違う概念で
使いドコロを誤らなければかなり強力です。
高速
想定よりも極めて速いです。
一般的に速度が問題になるようなことはありません。
デメリット
慣れないとちょっときついかもしれないです。保守の人とか他人に渡す場合のことも少しは考えたほうがいいかもしれません。
またデメリットというわけではないですがLINQ to SQLは個人的には推奨しません。
SQLほど利用者が多くないのではまってしまった場合解決が困難
予期せぬところでサーバに問い合わせてしまう場合があるという理由からです。
私が使いこなせないだけではありますが業務に使う分はちょっと厳しい気がしますね。
個人的にはDBを扱う場合はガッツリSQLを書きたいところです。
実践
細かい機能は紹介せずこういうときはこう使うんだよって!感じで説明していきます。
学校などの試験を例に考えてみます。
以下の様なデータを取り扱っていきます。
// データ構造
class Subject
{
public string Name { get; set; }
public int Mathematics { get; set; }
public int Physics { get; set; }
public int English { get; set; }
}
var testResults
= new List()
{
new Subject() { Name ="A", Mathematics = 90, Physics = 87, English = 70 },
new Subject() { Name ="B", Mathematics = 85, Physics = 89, English = 69 },
new Subject() { Name ="C", Mathematics = 98, Physics = 95, English = 82 },
new Subject() { Name ="D", Mathematics = 70, Physics = 67, English = 100 },
new Subject() { Name ="E", Mathematics = 57, Physics = 63, English = 38 }
};
人単位の数学、物理、英語の試験結果だと思ってください。合計得点を表示
var totalData =
from testResult in testResults
let Total = testResult.Mathematics + testResult.Physics + testResult.English
select new { testResult.Name, Total };
foreach( var totalDatum in totalData )
{
Console.WriteLine( totalDatum.Name + " さんの合計点は " + totalDatum.Total + "点です。");
}
実行結果
totalは小文字にするべきか大文字にするべきか...
簡単に解説すると各科目の合計をletを使って一時変数Totalに入れてNameとTotalを取得って感じかな?
基本的にコレクション全体にクエリが実行されます。
合計だけでなく様々な計算式、そして関数まで使えます。
任意の値でソート
var orderByEnglish =
from testResult in testResults
orderby testResult.English descending
select testResult;
Console.WriteLine("英語の点数の大きい順に表示します");
foreach (var datum in orderByEnglish)
{
Console.WriteLine(datum.Name + " さんの英語の点数は " + datum.English + "点です。");
}
実行結果
descendingが降順、つけない場合昇順になります。
これでDさん英語得意だな帰国子女かな?っていうのがわかります。(いや、帰国子女かどうかはわからないけど)
気軽に並び替えが可能なので仕様変更にも強い!!
ある科目に+5点
あ~ごめんごめん、物理の試験問題ミスってたわ~、全員に+5点ね。var physicsPlus5 =
from testResult in testResults
select new { testResult.Name, testResult.Mathematics, Physics = testResult.Physics+5, testResult.English };
Console.WriteLine("全員の物理の点数を+5点");
foreach (var datum in physicsPlus5)
{
Console.WriteLine(datum.Name + " さんの物理の点数は " + datum.Physics + "点です。");
}
実行結果
Physics は名前を定義し直す必要があります。
他も本当は必要なんですが省略しているだけです。(無名クラスというらしい)
科目ごとの平均点
var MathematicsAverage = testResults.Average(result => result.Mathematics);
var englishAverage = testResults.Average(result => result.English);
var physicsAverage = testResults.Average(result => result.Physics);
Console.WriteLine("数学の平均点は " + MathematicsAverage + " 点");
Console.WriteLine("英語の平均点は " + englishAverage + " 点");
Console.WriteLine("物理の平均点は " + physicsAverage + " 点");
実行結果
クエリ形式がちょっとわからなかった。
場合によって使いわけるといいと思います。
条件によって抽出
赤点(40点以下)を抽出してみる。var failingScoreStudents =
from testResult in testResults
where testResult.Mathematics < 40 || testResult.Physics < 40 || testResult.English < 40
select new { testResult.Name };
foreach (var datum in failingScoreStudents)
{
Console.WriteLine(datum.Name + " さんは赤点です。");
}
実行結果
キャプチャミスってしまった...
以上の例をlinqなしで書くことを想像してみてください。
LINQ...なかなか魅力的じゃないですか?