==プログラミングC#(2) LINQ==
[[言語 まとめ C#][C#][C# サンプルコード][Effective C# 4.0][Universal Windows Platform][Visual Studio]]
*プログラミングC# 第7版
*汎用 LINQ to Entities
====WCFデータサービスクライアント====
<<blockquote>>あらゆるオブジェクトコレクションに対してLINQを使用してみると非常に便利<</blockquote>>
===クエリ式(クエリ構文)===
*多くの種類のクエリを非常に自然な文法で記述できる
=====例1=====
int[] nums = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
IEnumerable<<int> > odd =
from num in nums
where num % 2 == 1
=====例2=====
var result = from book in books
where book.Title.IndexOf("計算") > > 0
orderby book.Title descending
select book.Title
=====データソース=====
*クエリのソースを確定
*IEnumerable/IEnumerable<<T> > インターフェース、またはその派生インターフェースを実装していることだけが条件
*foreachで処理できるオブジェクトであれば利用できる
**オブジェクト配列、データセット、XElementオブジェクト配列など
*出力を決定
*出力結果の型は、select句もしくはgroup句によって決まる
*すべてのクエリ式が IEnumerable<<T> > となるわけではない
**プロバイダによって決まる
**var を結果利用することが一般的
=====例1=====
int[] nums = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
IEnumerable<<int> > odd = nums .Where(num => > num % 2 == 1) .OrderByDescending( book=> > book.Title) .Select(num => > num);
foreach (var n in odd)
=====例2=====
var result = books
.Where( book => > book.Title.IndexOf("計算") > > 0 ) .Select( book => > book.Title)
;
*クエリ式で、サブ式の結果を格納して後の句で使用すると便利な場合があります。 let キーワードを使用すると、これを行うことができます
int[] nums = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
IEnumerable<<int> > odd =
from num in nums
let r = num % 2
===遅延評価===
*必要なときのみ動作するオブジェクトを返す
<<blockquote>>クエリの結果を取得しようとしたときのみ実際に処理が行われる<</blockquote>>
*無限シーケンスを処理できる
*クエリが何度も評価されないように注意する必要がある
===LINQ、ジェネリックとIQueryable<<T>>===
*ほとんどのLINQプロバイダでは、ジェネリック型が使われている
*LINQ to Objects では IEnumerable<<T> > が使われている*データベース用プロバイダでは、IQueryable<<T> > を使うものがある
==標準LINQ演算子==
*演算子とはLINQプロバイダが提供するクエリ機能のこと
**インデックスを伴うWhere演算子
int[] nums = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
IEnumerable<<int> > odd = nums.Where((r, idx) => > { Console.WriteLine($"IDX:{idx}"); return r % 2 == 1; });
*条件に合ったオブジェクトがない場合空のシーケンスを生成
====OfType<<T>>====
*特定の型の項目だけを取り出したいときに便利
var strings = src.OfType<<string>>();
*条件に合ったオブジェクトがない場合空のシーケンスを生成
====最終的に出力される項目を指定するラムダを渡すためにクエリの最後にselect句を書く====
*必要なプロパティだけを含む匿名型のインスタンスを返す
var pq = from product in dbCtx.Products
where (product.ListPrice > > 3000)
select new { product.Name, product.ListPrice, product.Size };
====射影とマップ====
int[] nums = { 1, 2, 3, 4, 5 };
char[] alphas = { 'a', 'b', 'c', 'd', 'e' };
IEnumerable<<string> > strs = from num in nums
from alpha in alphas
select alpha + num.ToString()
int[] nums = { 1, 2, 3, 4, 5 };
char[] alphas = { 'a', 'b', 'c', 'd', 'e' };
IEnumerable<<string> > strs = nums.SelectMany(num => > alphas, (num,alpha) => > alpha + num.ToString());
====取得列を明示的に指定====
*2つのオーバーロード
**項目を引数にとる
**--IList<<T>>を実装するコレクションの場合、IList<<T> > のContains が利用される**項目+IEqualityComparer<<T> > 型の引数をとる
====Any====
*コレクションが特定の条件を満たす値を1つ以上含んでいるか
*predicateをとる
**intを返す
condition.ResultCount = db.SearchConditionResults.Count(src => > src.SearchConditionId == condition.Id);
====LongCount====
*非常に大きな値の個数
|-
|}
空でない限りソースのコレクション全体が返される、空の場合 T型のゼロに相当する規定値参照型:null、数値型:0)を持つ一つの項目を含むシーケンスを返す,DefaultIfEmpty<<T>>
====集約====
{|class="wikitable"
====Max,Min====
*数値型のコレクションに備わる
double av = (new List<<int>>{12,123,5,6546,8,9,1}).Average();
*あらゆる型の項目に対して動作するオーバーロード版
**ラムダを引数に取る
var ttlAge = persons.Sum(person => > person.Age); var avAge = persons.Average(person => > person.Age); var mxAge = persons.Max(person => > person.Age); var mnAge = persons.Min(person => > person.Age);
====Aggregate====
**すべてを調べて1つの値を得る演算子を一般化
**--合計、最大、平均を書き換える
var s2 = persons.Aggregate(0.0, (ttl, person) => > ttl + person.Age); var m2 = persons.Aggregate(0.0, (max, person) => > max > > person.Age ? max : person.Age); var a2 = persons.Aggregate(new { ttl=0, cnt=0 }, (p1, person) => > new { ttl = p1.ttl + person.Age,cnt = p1.cnt + 1 }, p2 => > p2.ttl / p2.cnt);
**累積器
**reduce と呼ばれる機能のLINQにおける名称
string[] nums = { "1", "2", "3", "4", "5" };
string[] alphas = {"a", "b", "c", "d", "e" };
var zipped = nums.Zip(alphas, (num, alpha) => > num + ":" + alpha);
foreach( string s in zipped)
{
===グループ化===
====group句====
*IGrouping<<TKey,TItem> > を実装する項目のコレクションを生成する var persons = new List<<Person> > {
new Person() { Team="A",Name="hoge" },
new Person() { Team="B",Name="foo" },
**into キーワードによりクエリの残りで反復処理するための範囲変数が作られる
**--orderbyやwhereなどほかの句でも利用できる
**規定の結果IGrouping<<TKey,TItem>>も変更できる
var teams = from person in persons
group person by person.Team into team
select $"Team:{team.Key}:{team.Count()}";
====グループ射影を使ったグループ化クエリの展開====
var teams = persons.GroupBy(person => > person.Team) .Select(team => > $"Team{team.Key}:{team.Count()}");
**同じ意味を直接的に表すオーバーロド
**--2つのラムダをとる
**----2つめはグループオブジェクトを生成するため
**------最初の引数にKeyが渡る
var teams = persons.GroupBy(person => > person.Team, (teamName,team) => > $"Team{teamName}:{team.Count()}");
====キー、項目とグループの射影を使ったGroupBy演算子====
**3つのラムダをとる
**--各オブジェクトを生成
var teams = persons.GroupBy(
person => > person.Team, person => > person.Name, (teamName,names) => > $"Team{teamName}:{names.Count()}");
====複合グループキー====
**複数のキーでグループ化したい場合、単純にキーに両方の値を入れる
var persons = new List<<Person> > {
new Person() { Team="A",Name="hoge1",Lank="1" },
new Person() { Team="A",Name="hoge2",Lank="1" },
===Join===
*異なるソースから関連性のあるデータをクエリから使用できるようにする
var teams = new List<<Team> > {
new Team() {Code="A",Name="TeamA" },
new Team() {Code="B",Name="TeamB" },
new Team() {Code="C",Name="TeamC" }
};
var persons = new List<<Person> > {
new Person() { Team="A",Name="hoge1"},
new Person() { Team="B",Name="foo"},
}
====複合キーで結合する====
var teams = new List<<Team> > {
new Team() {Code="A",SubCode="1",Name="TeamASub1" },
new Team() {Code="B",SubCode="1",Name="TeamBSub1" },
new Team() {Code="C",SubCode="1",Name="TeamCSub1" }
};
var persons = new List<<Person> > {
new Person() { Team="A",TeamSub="1",Name="hoge1"},
new Person() { Team="B",TeamSub="1",Name="foo"},
===変換===
====シーケンスの型変換====
var sourceSeq = sequence.Cast<<Cource>>();====Cast<<T>>====
*変換不能な場合、例外をスローする
====OfType<<T>>====*Cast<<T> > とほとんど同じだが、間違った型が存在した場合、例外をスローせずフィルタする====AsEnumerable<<T>>====
*何ら変更なくソースを返す
*他のLINQプロバイダーによって処理される可能性があるものを扱う場合でも、確実にLINQ to Objectsによって処理されるようにする
*ここから先はクライアント側で処理をすると明示
====AsQueryable<<T>>====*AsEnumerable<<T>>の反対に、LINQ to Objectsの代わりに確実に自分で作成したクエリを使用したいようなシナリオ
====AsParallel====
*Parallel LINQ(PLINQ)によって実行されるクエリを構築するためのParallelQuery<<T> > が返される
====ToArray,ToList====
*入力クエリを実行した結果のすべてを格納した配列、リストが返される
====ToDictionary,ToLookup====
*連続ルックアップをサポートした結果を生成
*--IDictionay<<TKey,TValue>>,ILookup<<TKey,TValue>>が返される var persons = new List<<Person> > {
new Person() { Team="A",Name="hoge1"},
new Person() { Team="A",Name="hoge2"},
new Person() { Team="C",Name="fuga"}
};
var ta = persons.ToLookup(person => > person.Team);
foreach (var p in ta["A"])
{
==シーケンスの生成==
====Enumerable.Range====
*2つのintをとり、2つ目の引数まで1づつ大きくなるIEnumerable<<int>>を返す====Enumerable.Repeat<<T>>====
*型Tと回数を引数にとり、指定した回数生成する
====Enumerable.Empty<<T>>====*要素のないIEnumerable<<T>>が返される
==他のLINQの実装==
===Entity Framework===
*データベースとオブジェクト層を対応づけ
*複数のデータベースベンダ製品がサポート
*IQueryable<<T> > を使用**IEnemerable<<T> > から派生しているのでLINQ to Objectsの演算子を利用できる
*匿名メソッドやデリゲートは使用できない
====LINQ to SQL====
*SQL サーバー専用の設計
*.NET API として設計
*テーブルオブジェクトが、IQueryable<<T> > を実装
====WCF データサービスクライアント====
*Open Data Protocol(OData)を使用してHTTP上でデータを表現したり消費する
*XML もしくは JSON
*IQueryable<<T>>を使用
*一部のLINQ演算子のみサポート
===Parallel LINQ===