再版: C# の LINQ クエリ

目次

リンク

LINQとは

LINQプロバイダー

メソッド構文とクエリ構文

クエリ変数

クエリ式の構造

標準のクエリ演算子

LINQ to XML


リンク

LINQとは


リレーショナル データベース システムでは、データは適切に正規化されたテーブルに編成され、シンプルで強力な SQL 言語を通じてアクセスされます。データはテーブル内の特定の厳密なルールに従っているため、SQL はテーブル内で適切に機能します。 
ただし、データベースとは対照的に、プログラムでは、クラス オブジェクトまたは構造に格納されるデータは大きく異なります。したがって、データ構造からデータを取得するための汎用クエリ言語はありません。オブジェクトからデータを取得するメソッドは、常にプログラムの一部として設計されてきました。ただし、LINQ を使用すると、オブジェクトのコレクションを簡単にクエリできます。 
以下は、LINQ の重要な高度な機能です。

  • LINQ (リンクと発音) は、Language Integrated Query の略です
  • LINQ は、SQL を使用してデータベースをクエリするのと同じ方法でデータ コレクションをクエリできるようにする .NET フレームワークの拡張機能です。
  • LINQ を使用すると、データベース、プログラム オブジェクトのコレクション、XML ドキュメントのデータをクエリできます。

例: LINQ の例

クラスプログラム
{ 
    static void Main() 
    { 
        int[] 数値={2,12,5,15}; 
        IEnumerable<int> lowNums= 
                           n の数値
                           (n<10)から
                           n を選択します。
        foreach(var x in lowNums) 
        { 
            Console.WriteLine(x); 
        } 
} 
    }

LINQプロバイダー


前の例では、データ ソースは単なる int の配列であり、メモリ内のプログラムのオブジェクトです。ただし、LINQ はさまざまな種類のデータ ソースも操作できます。ただし、データ ソースの種類ごとに、そのデータ ソースの種類に基づいて LINQ クエリを実装するコード モジュールが背後に存在する必要があります。これらのコード モジュールは LINQ プロバイダーと呼ばれます。 
LINQ プロバイダーの重要なポイントは次のとおりです。

  • Microsoft は、いくつかの一般的なデータ ソース タイプ用の LINQ プロバイダーを提供しています
  • サードパーティは、さまざまなデータ ソース タイプに対応する LINQ プロバイダーを提供し続けています。

この章では、主に LINQ の紹介と、プログラム オブジェクト (LINQ to Object) および XML (LINQ to XML) への LINQ の使用方法について説明します。その他の詳細や使用方法については説明しません。

匿名型

LINQ クエリ機能の詳細に入る前に、まず名前のないクラス型を作成できる機能について学びます。匿名型は、LINQ クエリの結果でよく使用されます。 
第 6 章では、オブジェクト初期化ステートメントを紹介します。これにより、オブジェクト作成式を使用するときに新しいクラス インスタンスのフィールドとプロパティを初期化できるようになります。この形式のオブジェクト作成式は、new キーワード、クラス名またはコンストラクター、オブジェクト初期化ステートメントの 3 つの部分で構成されていることに注意してください。オブジェクト初期化ステートメントには、一連の中括弧内にメンバー初期化のコンマ区切りリストが含まれます。 
匿名型の変数の作成には同じ形式を使用しますが、クラス名とコンストラクターは使用しません。次のコード行は、匿名型のオブジェクト作成式を示しています。

クラス名なし
new {FieldProp=InitExpr,FieldProp=InitExpr,...} 
        メンバ初期化文

例: 匿名型の作成と使用の例。

class Program 
{ 
    static void Main() 
    {
     必ず使用するvar 
        var Student=new{Name="メアリー・ジョーンズ",年齢=19,専攻=歴史"}; 
        Console.WriteLine("{0},年齢 {1},専攻: {2}",student.Name,student.Age,studeng.Major); 
    } 
}

匿名型について知っておくべき重要な点がいくつかあります。

  • 匿名型はローカル変数でのみ使用でき、クラス メンバーでは使用できません。
  • 匿名型には名前がないため、変数の型として var キーワードを使用する必要があります。
  • 匿名型オブジェクトのプロパティは設定できません。コンパイラによって匿名型に対して作成されたプロパティは読み取り専用です

コンパイラは、匿名型のオブジェクト初期化ステートメントを検出すると、名前付きの新しいクラス型を作成します。各メンバー初期化ステートメントの下で、その型を推論し、その値にアクセスするための読み取り専用プロパティを作成します。プロパティとメンバーの初期化ステートメントは同じ名前を持ちます。匿名型が構築された後、コンパイラはこの型のオブジェクトを作成します。 
オブジェクト初期化ステートメントの代入形式に加えて、匿名型のオブジェクト初期化ステートメントには、単純な識別子とメンバー アクセス式という 2 つの形式が許可されています。これら 2 つの形式は、プロジェクション初期化子と呼ばれます。以下の変数宣言は 3 つの形式を示しています。

var Student=new{年齢=19,その他.名前,専攻};

例: 合計 3 つの初期化ステートメントを使用します。投影された初期化ステートメントは、匿名型宣言の前に定義する必要があることに注意してください。

class Other 
{ 
    static public string Name="メアリー・ジョーンズ"; 
}
クラス プログラム
{ 
    static void Main() 
    { 
        string Major="History"; 
        var Student=new{年齢=19,その他.名前,専攻}; 
        Console.WriteLine("{0},年齢 {1},専攻: {2}",student.Name,student.Age,studeng.Major); 
    } 
}

コンパイラーは、同じパラメーター名、同じ推論型、および同じ順序を持つ別の匿名型を検出した場合、この型を再利用し、新しい匿名型を作成せずに新しいインスタンスを直接作成します。

メソッド構文とクエリ構文


LINQ クエリを作成するときは、メソッド構文とクエリ構文という 2 つの形式の構文を使用できます。

  • メソッド構文では標準のメソッド呼び出しを使用します。これらのメソッドは、標準クエリ演算子のセットのメソッドです。
  • クエリ構文は SQL ステートメントに似ています
  • 1 つのクエリで両方の形式を組み合わせることができます

メソッド構文は必須であり、クエリ メソッドが呼び出される順序を指定します。 
クエリ構文は宣言型です。つまり、クエリは何を返したいかを記述しますが、クエリの実行方法は指定しません。 
コンパイラは、クエリ構文を使用して表現されたクエリをメソッド呼び出しに変換します。実行時には 2 つの形式の間にパフォーマンスの違いはありません。 Microsoft では、クエリ構文の方が読みやすく、クエリの意図が明確に示され、エラーが発生しにくいため、クエリ構文の
使用を推奨しています。ただし、一部の演算子はメソッド構文を使用して記述する必要があります。

例: メソッド構文とクエリ構文のデモンストレーション

class Program 
{ 
    static void Main() 
    { 
        int[]数値={2,5,28,31,17,16,42}; 
        var numsQuery=from n in数値 //クエリ構文
                      where n<20 
                      select n; 
        var numsMethod =numbers.Where(x=>x<20); //メソッド構文
        int numsCount=(from n innumbers // 
                       n<20 の2 つの形式の組み合わせ
                       select n).Count(); 
        foreach(var x in numsQuery) 
        { 
            Console.Write("{0}, ",x); 
        } 
        Console.WriteLine(); 
        foreach(var x in numsMethod) 
        {
            Console.Write("{0}, ",x); 
        Console.WriteLine 
        (); 
        Console.WriteLine(numsCount); 
    } 
}

クエリ変数


LINQ クエリは 2 種類の結果を返すことができます - クエリ パラメーターを満たす項目のリストである列挙型 (列挙型ではなく、列挙可能なデータのセット)、または scalar と呼ばれる単一の値のいずれかです。クエリ条件を満たす結果の要約形式。

例: クエリ変数の例

int[]numbers={2,5,28}; IEnumerable<int> lowNums=from n in数値 // 
                         n<20 の
列挙番号を返します
                         select n; 
int numsCount=(from n in数値 //整数を返し
               ますn<20 
               select n).Count();

クエリ変数の使用法を理解することが重要です。前のコードを実行すると、lowNums クエリ変数にはクエリの結果が含まれなくなります。代わりに、コンパイラはクエリを実行するコードを作成します。 
クエリ変数 numCount には実際の整数値が含まれており、実際にクエリを実行することによってのみ取得できます。 
違いはクエリの実行時間にあり、これは次のように要約できます。

  • クエリ式が列挙を返す場合、その列挙が処理されるまでクエリは実行されません。
  • 列挙が複数回処理される場合、クエリも複数回実行されます。
  • トラバーサルの実行後、クエリの実行前にデータが変更された場合、クエリは新しいデータを使用します。
  • クエリ式がスカラーを返す場合、クエリは直ちに実行され、結果はクエリ変数に格納されます。

クエリ式の構造


クエリ式は、クエリ本体の後の from 句で構成されます。クエリ式について知っておくべき重要な点がいくつかあります。

  • 句は特定の順序で出現する必要があります
  • from 句と select...group 句の 2 つの部分は必須です
  • LINQ クエリ式では、select 句は式の最後にあります。C# がこれを行う理由の 1 つは、コードを入力するときに Visual Studio IntelliSense がより多くのオプションを提供できるようにするためです。
  • from…let…where句はいくつでも使用できます

from 句

from 句は、データ ソースとして使用されるデータ コレクションを指定します。反復変数も導入します。from 句の重要なポイントは次のとおりです。

  • データ ソースの各要素を表す変数を 1 つずつ繰り返します
  • from句の構文は次のとおりです。
    • Type はコレクション内の要素のタイプです。コンパイラはコレクションから型を推測できるため、これはオプションです。
    • 項目は反復変数の名前です
    • Items は、クエリ対象のコレクションの名前です。コレクションは列挙可能である必要があります。第 18 章を参照してください。
アイテムのタイプアイテムから

次の図は、from 句の構文を示しています。型指定子はオプションです。任意の数の結合句を含めることができます。 

LINQ の from 句と foreach ステートメントは非常に似ていますが、主な違いは次のとおりです。

  • foreach ステートメントは、コレクション内の項目が最初から最後まで順番にアクセスされることを強制的に指定します。from 句は、コレクション内の各項目にアクセスする必要があることを宣言的に規定しますが、どのような順序でアクセスするかは想定しません。
  • foreach ステートメントはコードに遭遇すると本体を実行しますが、from 句は何も実行しません。クエリは、プログラムの制御フローがクエリ変数にアクセスするステートメントに遭遇した場合にのみ実行されます。

join子句

LINQ の join 句は、SQL の JOIN 句に似ています。違いは、データベース テーブルだけでなくコレクション オブジェクトに対しても結合を実行できることです。結合を初めて使用する場合は、次のことが考えを明確にするのに役立ちます。 
まず、関連付けの構文を理解する必要があります。

  • 結合を使用して、2 つ以上のコレクションのデータを結合します
  • 結合操作は 2 つのコレクションを受け取り、それぞれに元のコレクション オブジェクトのすべてのフィールドが含まれるオブジェクトの一時コレクションを作成します。

結合構文は次のとおりです

キーワード キーワード キーワード キーワード
 ↓ ↓ ↓ ↓ 
join Identifier in Collection2 on Field1 = Field1 
              別のコレクションと ID を指定して参照します
var query=from s in students 
          join c in studentsInCourses on s.StID = c.StID

つながりとは何ですか

LINQ での結合は 2 つのコレクションを受け取り、各要素に 2 つの元のコレクションの元のメンバーが含まれる新しいコレクションを作成します。

例: 結合例 

クラス プログラム
{ 
    public クラス Student 
    { 
        public int StID; 
        パブリック文字列LastName; 
    }
    パブリッククラス CourseStudent 
    {
        パブリック文字列 CourseName; 
        パブリック int StID; 
    } 
    static Student[] students=new Student[]{ 
        new Student{StID=1,LastName="Carson"}, 
        new Student{StID=2,LastName="Klassen"}, 
        new Student{StID=3,LastName="フレミング"}, 
    }; 
    static CourseStudent[] studentsInCourses=new CourseStudent[]{ 
        new CourseStudent{CourseName="Art",StID=1},  
        new CourseStudent{CourseName="Art",StID=2},
        new CourseStudent{CourseName="History",StID=1}, 
        new CourseStudent{CourseName="History",StID=3}, 
        new CourseStudent{CourseName="物理学",StID=3}, 
    } 
    static void Main() 
    { 
        var query=from s in students 
                  join c in studentsInCourses on s.StID = c.STID 
                  where c.CourseName=="History" 
                  select.LastName; 
        foreach(var q in query) 
        { 
            Console.WriteLine("学生が履歴を取得:{0}",q); 
        } 
} 
    }

クエリ本文の from…let…where フラグメント

オプションの from...let...where 部分はクエリ本体の最初の部分で、from 句、let 句、where 句の 3 つの句を任意の数で組み合わせることができます。

from 句

クエリ式は必須の from 句で始まり、その後にクエリ本体が続きます。本体自体は他の from 句で始めることができます。各 from 句は追加のソース データ セットを指定し、後で操作される反復変数を導入します。すべての from 句の構文と意味は同じです。 

例: from 句の例

クラスプログラム
{ 
    static void Main() 
    { 
        var groupA=new[]{3,4,5,6}; 
        var groupA=new[]{6,7,8,9}; 
        var someInts=from a in groupA 
                     from b in groupB 
                     where a>4&&b<=8 
                     select new{a,b,sum=a+b};//匿名类型对象
        foreach(var a in someInts) 
        { 
            Console.WriteLine(a) ); 
        } 
} 
    }

let句

let 句は式演算を受け入れ、それを他の演算で使用する必要がある識別子に割り当てます。let 句の構文は次のとおりです。

let 識別子=式

例: let 句の例

class Program 
{ 
    static void Main() 
    { 
        var groupA=new[]{3,4,5,6}; 
        var groupA=new[]{6,7,8,9}; 
        var someInts=from a in groupA 
                     from b in groupB 
                     let sum=a+b //結果を新しい変数に保存します
                     where sum==12 
                     select new{a,b,sum}; 
        foreach(var a in someInts) 
        { 
            Console.WriteLine(a); 
        } 
    } 
}

where子句

where 句は、後続の操作に基づいて指定された項目をフィルタリングします。 
from...let...where 部分にある限り、クエリ式には複数の where を含めることができます。

例: where 句の例

クラスプログラム
{ 
    static void Main() 
    { 
        var groupA=new[]{3,4,5,6}; 
        var groupA=new[]{6,7,8,9}; 
        var someInts=from a in groupA 
                     from b in groupB 
                     let sum=a+b          
                     where sum>=11 ← 条件 1 
                     where a==4 ← 条件 2 
                     select new{a,b,sum}; 
        foreach(var a in someInts) 
        { 
            Console.WriteLine(a); 
        } 
} 
    }

orderby句

orderby 句は、式に基づいて結果項目を順番に返します。 
orderby 句の構文は次のとおりです。オプションの昇順および降順キーワードは、並べ替え方向を設定します。通常、式は項目のフィールドです。フィールドは数値フィールドである必要はなく、文字列などの並べ替え可能なタイプにすることもできます。

  • orderby 句のデフォルトは昇順です
  • 節はいくつでも指定できますが、カンマで区切る必要があります

例: 学生の年齢順に並べ替える

class Program 
{ 
    static void Main() 
    { 
        var students=new[] 
        { 
            new{LName="ジョーンズ",FName="メアリー",年齢=19,メジャー="歴史"}, new{ 
            LName="スミス",FName ="ボブ",年齢=20,専攻=”コンプサイエンス”}, 
            new{LName=”フレミング”,FName=”キャロル”,年齢=21,専攻==”歴史”}, } 
        ; 
        var query=fromstudent in students 
                  orderbystudent.年齢で
                  学生を選択します。
        foreach(クエリ内の変数) 
        { 
            Console.WriteLine("{0},{1}: {2} - {3}",s.LName,s.FName,s.Age,s. 選考科目); 
        } 
} 
    }

select…group 句

select...group 句の機能は次のとおりです。

  • select 句は、選択されたオブジェクトのどの部分を選択するかを指定します。次のいずれかを指定できます
    • データ項目全体
    • データ項目のフィールド
    • データ項目の複数のフィールド (または同様の他の値) で構成される新しいオブジェクト
  • group...by 句はオプションで、選択した項目をグループ化する方法を指定するために使用されます。

例: データ項目全体を選択する

システムを使用する; 
System.Linq を使用します。
class Program 
{ 
    static void Main() 
    { 
        var students=new[] 
        { 
            new{LName="ジョーンズ",FName="メアリー",年齢=19,メジャー="歴史"}, new{ 
            LName="スミス",FName ="ボブ",年齢=20,専攻=”コンプサイエンス”}, 
            new{LName=”フレミング”,FName=”キャロル”,年齢=21,専攻==”歴史”}, } 
        ; 
        var query=from s in students 
                  select s; 
        foreach(クエリ内の変数) 
        { 
            Console.WriteLine("{0},{1}: {2} , {3}",s.LName,s.FName,s.Age,s. 選考科目); 
        } 
} 
    }

var query=from s in students 
          select s.LName; 
foreach(クエリ内の変数) 
{ 
    Console.WriteLine(s); 
}

クエリ内の匿名型

クエリ結果は、元のコレクションの項目、項目の一部のフィールド、または匿名型で構成されます。 
例: select を使用して匿名型を作成する

システムを使用する; 
System.Linq を使用します。
class Program 
{ 
    static void Main() 
    { 
        var students=new[] 
        { 
            new{LName="ジョーンズ",FName="メアリー",年齢=19,メジャー="歴史"}, new{ 
            LName="スミス",FName ="ボブ",年齢=20,専攻=”コンプサイエンス”}, 
            new{LName=”フレミング”,FName=”キャロル”,年齢=21,専攻==”歴史”}, } 
        ; 
        var query=from s in students 
                  select new{s.LName,s.FName,s.Major}; 
        foreach(クエリ内の変数) 
        { 
            Console.WriteLine("{0} {1} -- {2} , {3}",s.FName,s.LName,s. 選考科目); 
        } 
} 
    }

群節

group 句は、選択したオブジェクトをいくつかの基準に従ってグループ化します。たとえば、前の例の学士の配列を使用すると、プログラムを専攻コースに従ってグループ化できます。

  • クエリの結果に項目が含まれている場合は、フィールドの値に基づいてグループ化できます。グループ化の基準となる属性をキーと呼びます
  • group 句は、元のデータ ソース内の項目の列挙を返しませんが、形成された項目のグループを列挙できる列挙型を返します。
  • グループ自体は列挙可能なタイプであり、実際の項目を列挙できます。

例:学士の専攻科目によるグループ分け

システムを使用する; 
System.Linq を使用します。
class Program 
{ 
    static void Main() 
    { 
        var students=new[] 
        { 
            new{LName="ジョーンズ",FName="メアリー",年齢=19,メジャー="歴史"}, new{ 
            LName="スミス",FName ="ボブ",年齢=20,専攻=”コンプサイエンス”}, 
            new{LName=”フレミング”,FName=”キャロル”,年齢=21,専攻==”歴史”}, } 
        ; 
        var query=from s 学生の
                  グループ s by s.Major; 
        foreach(クエリ内の変数) 
        { 
            Console.WriteLine("{0}",s. 鍵); 
            foreach(var t in s)  
            {
                Console.WriteLine(" {0},{1}",t.LName,t.FName);
            } 
        } 
} 
    }

クエリの継続: into 句

クエリ継続句では、クエリの一部の結果を取得し、クエリの別の部分で使用できるように名前を付けることができます。 
 
例: groupA と groupB を接続し、groupAandB という名前を付ける

クラスプログラム
{ 
    static void Main() 
    { 
        var groupA=new[]{3,4,5,6}; 
        var groupA=new[]{6,7,8,9}; 
        var someInts=groupA の a から
                     groupB の b に参加します。 a が b に等しい場合に
                     groupAandB に入力します。 
                     groupAandB の c から
                     c を選択します。
        foreach(var a in someInts) 
        { 
            Console.WriteLine(a); 
        } 
} 
    }

出力: 6

標準のクエリ演算子


標準のクエリ演算子は、任意の .NET 配列またはコレクションをクエリできるようにする一連の API メソッドで構成されています。 
標準クエリ演算子の重要な機能は次のとおりです。

  • クエリされるコレクション オブジェクトはシーケンスと呼ばれ、IEnumerable<T>インターフェイスを実装する必要があり、T は型です。
  • 標準のクエリ演算子の使用構文
  • 一部の演算子は IEnumerable オブジェクト (または他のシーケンス) を返しますが、他の演算子はスカラーを返します。スカラーを返す演算子はすぐに実行され、値を返します。
  • 多くの操作は述語をパラメータとして受け取ります。述語は、オブジェクトをパラメータとして受け取り、オブジェクトが特定の条件を満たすかどうかに応じて true または false を返すメソッドです。

例: Sum 演算子と Count 演算子の使用

クラス プログラム
{ 
    static int[] 数値 = new int[]{2,4,6}; 
    static void Main() 
    { 
        int total=numbers.Sum(); 
        int howMany=number.Count(); 
        Console.WriteLine("合計: {0},カウント: {1}",total,howMany); 
    } 
}

標準のクエリ演算子を使用して、1 つ以上のシーケンスを操作できます。Sequence は、 List<>、Dictionary<>、Stack<>、Array などの IEnumerable<> インターフェイスを実装する型を指します。 

標準クエリ演算子の署名

System.Linq.Enumerable クラスは、標準のクエリ演算子メソッドを宣言します。IEnumerable<T>これらのメソッドは単なるメソッドではなく、ジェネリック クラス を拡張する拡張メソッドです。
第 7 章と第 17 章ではクラス拡張メソッドを紹介しているため、このセクションは拡張メソッドの使用方法を学ぶ良い機会です。 
簡単なレビューです。拡張メソッドはパブリック静的メソッドであり、1 つのクラスで定義されていますが、別のクラス (最初の仮パラメーター) に機能を追加することを目的としています。このパラメータの前にはキーワード this を付ける必要があります。

例: 3 つの標準クエリ演算子の署名

常に public static 名とジェネリック パラメーターの最初のパラメーター
     ↓ ↓ ↓ 
public static int Count<T>(この IEnumerable<T> ソース); 
public static T First<T>(この IEnumerable<T> ソース); 
public static IEnumerable<T > Where<T>(この IEnumerable<T> ソース,...);

例: 拡張メソッドを直接呼び出すことと、拡張メソッドとして呼び出すことの違い

System.Linq を使用します。
... 
static void Main() 
{ 
    int[] intArray=new int[]{3,4,5,6,7,9}; 
    //メソッド语法
    var count1=Enumerable.Count(intArray); 
    var firstNum1=Enumerable.First(intArray) 
    //展示语法
    var count2=intArray.Count(); 
    var firstNum2=intArrya.First(); 
    Console.WriteLine("カウント: {0},FirstNumber: {1}",count1,firstNum1); 
    Console.WriteLine("カウント: {0},FirstNumber: {1}",count2,firstNum2); 
}

クエリ式と標準クエリ演算子

クエリ式とメソッド構文は組み合わせることができます。コンパイラは、各クエリ式を標準のクエリ演算子の形式に変換します。

class Program 
{ 
    static void Main() 
    { 
        varnumber=new int[]{2,6,4,8,10}; 
        int howMany( 
                    n < 7 の場合、
                    n から n を選択).Count(); 
        Console.WriteLine("カウント: {0}",howMany); 
    } 
}

デリゲートをパラメータとして渡す

各演算子の最初のパラメータはIEnumerable<T>オブジェクトへの参照であり、後続のパラメータは任意の型であることができることを前に説明しました。多くの演算子はパラメータとして汎用デリゲートを受け入れます (第 17 章)。汎用デリゲートは、ユーザー定義コードを演算子に提供するために使用されます。

これを説明するために、Count 演算子の使用方法をいくつか示す例から始めましょう。 
Count 演算子はオーバーロードされており、2 つの形式があります。最初の形式は前の例で使用されました。この演算子には 1 つのパラメーターがあり、コレクション内の要素の数を返します。

public static int Count<T>(この IEnumerable<T> ソース);

ただし、配列内の奇数要素の合計数を確認したいとします。Count メソッドは、整数が奇数であるかどうかを検出できなければなりません。 
Count メソッドの 2 番目の形式を使用する必要があります。以下に示すように、パラメーターとして汎用デリゲートがあります。呼び出されると、型 T の単一の入力パラメータを受け入れ、ブール値を返すデリゲート オブジェクトが提供されます。デリゲート コードの戻り値は、要素が合計に含まれるかどうかを指定する必要があります。

public static int Count<T>(この IEnumerable<T> ソース,Func<T,bool> 述語);
class Program 
{ 
    static void Main() 
    { 
        int[] intArray=new int[] {3,4,5,6,7,9}; 
        var countOdd=intArray.Count(n=>n%2!=0); 
        Console.WriteLine("奇数の数: {0}",countOdd); 
    } 
}

LINQ の事前定義されたデリゲート型

前の例の Count 演算子と同様に、多くの LINQ 演算子では、演算子の実行方法を演算子に指示するコードを提供する必要があります。これを行うには、デリゲート オブジェクトをパラメータとして使用します。 
LINQ では、標準クエリ演算子で使用する汎用デリゲート型の 2 セット、つまり Func デリゲートと Action デリゲートが定義されており、それぞれ 17 個のメンバーがあります。

  • 引数として使用するデリゲート オブジェクトは、次の型または次の形式のいずれかである必要があります。
  • TR は戻り値を表し、常に型パラメータ リストの最後の値になります。
パブリック デリゲート TR Func<in T1,in T2,out TR>(T1 a1,T2 a2); 
                 ↑ ↑ ↑
              戻り値の型 type パラメータ メソッド パラメータ

戻り値の型パラメーターには out キーワードがあり、共変になっていることに注意してください。つまり、宣言された型またはこの型から派生した型を受け入れることができます。入力パラメータには in キーワードがあり、反変になります。つまり、宣言された型またはこの型から派生した型を受け入れることができます。

デリゲートパラメータの使用例

クラス プログラム
{ 
    static bool IsOdd(int x) 
    { 
        return x%2!=0; 
    静的
    void Main() 
    { 
        int[] intArray=new int[] {3,4,5,6,7,9}; 
        Func<int,bool>myDel=new Func<int,bool>(IsOdd); 
        var countOdd=intArray.Count(myDel); 
        Console.WriteLine("奇数の数: {0}",countOdd); 
    } 
}

Lamba 式パラメータを使用した例

前の例では、別のメソッドとデリゲートを使用してコードを演算子にアタッチしました。これには、メソッドとデリゲート オブジェクトを宣言し、デリゲート オブジェクトをオペレーターに渡す必要があります。この方法は、次の条件のいずれかに該当する場合に適した解決策です。

  • このメソッドは、デリゲート オブジェクトの初期化に使用される場所だけでなく、プログラム内の他の場所でも呼び出す必要があります。
  • 関数本体に複数のコードステートメントがあります

これらの条件が両方とも当てはまらない場合は、演算子にコードを提供する、よりクリーンでローカライズされた方法、つまり Lambda 式を使用することをお勧めします。

例: ラムダ式を使用して前の例を変更する

class Program 
{ 
    static void Main() 
    { 
        int[] intArray=new int[] {3,4,5,6,7,9}; 
        var countOdd=intArray.Count(n=>n%2!=0);//Lambda表达式
        Console.WriteLine("奇数の数: {0}",countOdd); 
    } 
}

ラムダ式の代わりに匿名メソッドを使用することもできます。ただし、このアプローチは面倒であり、ラムダ式は意味的には匿名メソッドと同等であり、より簡潔であるため、匿名メソッドを使用する理由はもうありません。

class Program 
{ 
    static void Main() 
    { 
        int[] intArray=new int[] {3,4,5,6,7,9}; 
        Func<int,bool> myDel=delegate(int x) //匿名メソッド
                             { 
                                 return x%2!=0; 
                             }; 
        var countOdd=intArray.Count(myDel); 
        Console.WriteLine("奇数の数: {0}",countOdd); 
    } 
}

LINQ to XML


Extensible Markup Language (XML) は、データを保存および交換するための重要な方法です。LINQ は、XPath や XSLT よりも XML をはるかに使いやすくする機能を言語に追加します。

  • XML ツリーは単一のステートメントを使用して上から下まで作成できます
  • ツリーを含む XML ドキュメントを使用する代わりに、メモリ内で XML を作成して操作できます。
  • Text サブノードを使用する代わりに、文字列ノードを作成して操作できます。
  • XML ツリーを検索するときに、XML ツリーをたどる必要はありません。ツリーにクエリを実行するだけで、必要な結果が返されます。

本書は XML の完全な入門書ではありませんが、LINQ to XML を導入する前に XML について簡単に説明します。

マークアップ言語

マークアップ言語は、ドキュメントに関する情報を提供し、そのコンテンツを編成するドキュメント内のタグのセットです。つまり、マークアップ タグはドキュメントに関するデータではなく、データに関するデータが含まれています。データに関するデータはメタデータと呼ばれます。 
マークアップ言語は、ドキュメントのコンテンツに関する特定の種類のメタデータを伝達するために設計された定義済みのタグのセットです。たとえば、HTML はよく知られたマークアップ言語です。タグ内のメタデータには、Web ページがブラウザーでどのようにレンダリングされるか、およびページ内を移動するためにハイパーリンクがどのように使用されるかに関する情報が含まれています。 
XML には事前定義されたタグが少数しかありません。その他のタグは、特定のドキュメント タイプに必要なメタデータを表すためにプログラマによって定義されます。データの読み取りと書き込みの両方がラベルの意味を知っている限り、ラベルには設計者が望むあらゆる有用な情報を含めることができます。

XMLの基本

XML ドキュメント内のデータには、主にネストされた要素で構成される XML ツリーが含まれています。 
要素はXML ツリーの基本要素です。各要素には名前があり、データが含まれており、一部の要素には他のネストされた要素が含まれています。要素は開始タグと終了タグによって分割されます。データを含む要素は、開始タグと終了タグの間にある必要があります。

  • 開始タグ <ElementName>
  • 終了タグ </ElementName>
  • コンテンツのない単一のタグ <ElementName/>

例:

   開始タグの内容 終了タグ
      ↓ ↓ ↓ 
<EmployeeName>Sally Jones</EmployeeName> 
<PhoneNumber/> ←内容のない要素

XML について知っておくべき重要な点:

  • XML ドキュメントには、他のすべての要素を含むルート要素が必要です
  • XML タグは適切にネストする必要があります
  • HTML タグとは異なり、XML タグでは大文字と小文字が区別されます。
  • XML 属性は、要素に関する追加のメタデータを含む名前と値のペアです。属性の値の部分は引用符で囲む必要があります。一重引用符または二重引用符を使用できます。
  • XML ドキュメント内の空白は有効です。これは、スペースをスペースとして出力する HTML とは異なります。
<従業員> 
    <従業
        員> <名前>ボブ スミス</
        名前> <電話番号>408-555-1000< /電話番号
        > <携帯電話/> </従業員> <
    従業
        員> <名前>サリー ジョーンズ</名前> <
    電話
        番号> 415-555-2000</PhoneNumber> 
        <PhoneNumber>415-555-2001</PhoneNumber> </従業
    員> 
</従業員>

XMLクラス

LINQ to XML は、2 つの方法で XML とともに使用できます。1 つ目は、簡素化された XML 操作 API としての方法であり、2 つ目は、この章で前述した LINQ クエリ ツールを使用する方法です。 
まずはAPIメソッドを紹介します。 
LINQ to XML API は、XML ツリー コンポーネントを表す多くのクラスで構成されます。主に XElement、XAttribute、XDocument の 3 つのクラスを使用します。 
以下の図は、XML ツリーの構築に使用されるクラスと、それらがどのようにネストされるかを示しています。

  • XDocument ノードの直接の子として使用可能
    • ほとんどの場合、XDeclaration ノード、XDocumentType ノード、および XElement ノードの各ノード タイプが 1 つずつ存在します。
    • 任意の数の XProcessingstruct ノード
  • XDocument 内に最上位の XElement ノードがある場合、それは XML ツリー内の他の要素のルートになります。
  • ルート要素には、任意のレベルでネストされた、任意の数の XElement、XComment、または XProcessingstruct ノードを含めることができます。

XAttribute クラスを除いて、XML ツリーの作成に使用されるクラスのほとんどは、XNode というクラス (本書では「XNodes」と呼ばれることが多い) を継承します。

明示的な XML ドキュメントの作成、保存、ロード、および

例: Employees ノードを含む XML ツリーを作成する

using System; 
using System.Xml.Linq; 
class Program 
{ 
    static void Main() 
    { 
        XDocumentemployees1= 
            new XDocument( //XML ドキュメントを作成
                new XElement("Employees", 
                    new XElement("Name","Bob Smith"), 
                    new XElement("Name","Sally Jones") 
                ) 
            ); 
        employees1.Save("EmployeesFile.xml"); //ファイル
        XDocument に保存employees2=XDocument.Load("EmployeesFile.xml"); 
                                   静的メソッド
        Console。WriteLine(employees2); //明示的なファイル
    }
}

XMLツリーの作成

例: XML ツリーの作成

システムを使用する; 
System.Xml.Linq を使用します。
class Program 
{ 
    static void Main() 
    { 
        XDocumentemployeeDoc= 
            new XDocument( //创建XML文档
                new XElement("従業員", 
                    new XElement("従業員", 
                        new XElement("名前","ボブ・スミス"), 
                        new XElement( "PhoneNumber","408-555-1000")), 
                    new XElement("従業員", 
                        new XElement("Name","Sally Jones"), 
                        new XElement("PhoneNumber","415-555-2000"),
                ) 
            ); 
        Console.WriteLine(従業員ドキュメント); 
    } 
}

XML ツリーの値の使用

XML の威力は、XML ツリーを走査して値を取得または変更するときに発揮されます。次の表に、データを取得するために使用される主な方法を示します。 
 
上の表に関して、次の点に注意してください。

  • ノード Nodes メソッドはIEnumerable<object>、返されるノードの型が XElement、XComment など異なる場合があるため、 型のオブジェクトを返します。型をパラメータとして受け取るメソッドを使用して、OfType(type)特定の型のノードを返すように指定できます。たとえば、次のコードは XComment ノードのみを取得できます。
    • IEnumerable<XComment> comments=xd.Nodes().OfType<XComment>()
  • 要素 XElement の取得は非常に一般的なニーズであるため、`Nodes.OfType(XElement)()` 式の短縮形、Elements メソッドが登場しました。
    • パラメーターのない Elements メソッドは、すべての子 XElement を返します。
    • 単一の name パラメーターを持つ Elements メソッドは、その名前を持つ子 XElement を返します。たとえば、次のコードは、PhoneNumber という名前の子 XElement ノードを返します。
    • IEnumerable<XElement> empPhones=emp.Elements("PhoneNumber");
  • Element このメソッドは、現在のノードの最初の子 XElement のみを取得します。パラメータがない場合は最初の XElement ノードを取得し、パラメータが 1 つある場合は、この名前の最初の子 XElement を取得します。
  • 子孫と祖先 これらのメソッドは、Elements メソッドと Parent メソッドに似ていますが、直接の子要素と親要素を返さず、その上下のすべてのノードを含むネスト レベルを無視する点が異なります。
システムを使用する; 
System.Collections.Generic を使用します。
System.Xml.Linq を使用します。
class Program 
{ 
    static void Main() 
    { 
        XDocumentemployeeDoc= 
            new XDocument( //创建XML文档
                new XElement("従業員", 
                    new XElement("従業員", 
                        new XElement("名前","ボブ・スミス"), 
                        new XElement( "電話番号","408-555-1000"))、
                    新しい XElement("従業員",
                        新しい XElement("名前","サリー ジョーンズ")、
                        新しい XElement("電話番号",
                        new XElement("電話番号","415-555-2001")) ) 
                ) 
            ; 
        // 最初の名前は「Employees」の子 XElement 
        XElement root=employeeDoc.Element("Employees"); 
        IEnumerable<XElement> 従業員=root.Elements(); 
        foreach(従業員の XElement emp) 
        { 
            XElement empNameNode=emp.Element("名前"); 
            Console.WriteLine(empNameNode.Value); 
            IEnumerable<XElement> empPhones=emp.Elements("PhoneNumber"); 
            foreach(empPhones の XElement 電話) 
            { 
                Console.WriteLine(phone. 価値); 
            } 
    } 
        }
}

ノードを追加して XML を操作する

Add メソッドを使用して、既存の要素に子要素を追加できます。

システムを使用する; 
System.Xml.Linq を使用します。
class Program 
{ 
    static void Main() 
    { 
        XDocument xd=new XDocument( 
            new XElement("root", 
                new XElement("first") 
            ) 
        ); 
        Console.WriteLine("元のツリー"); 
        Console.WriteLine(xd); 
        Console.WriteLine(); 
        XElement rt=xd.Element("ルート"); 
        rt.Add(新しい XElement("2 番目")); 
        rt.Add(new XElement("3 番目"), 
               new XComment("重要なコメント"), 
               new XElement("4 番目")); 
        コンソール。
        Console.WriteLine(xd); 
    } 
}

次の表に、XML を操作する最も重要な方法のいくつかを示します。 

XML属性を使用する

属性は、XElement ノードに関する追加情報を提供し、XML 要素の開始タグに配置されます。 
関数型メソッドを使用して XML ツリーを構築する場合、XElement のコンストラクターに XAttribute コンストラクターを含めて機能を追加するだけで済みます。XAttribute コンストラクターには 2 つの形式があります。1 つは名前と値を受け入れ、もう 1 つは既存の XAttribute への参照を受け入れます。

例: ルートに 2 つの機能を追加します。

XDocument xd=new XDocument( 
    new XElement("root", 
            new XAttribute("color","re​​d"), 
            new XAttribute("size","large"), 
        new XElement("first"), 
        new XElement(" first") ") 
    ) 
);

例: プロパティの取得

class Program 
{ 
    static void Main() 
    { 
        XDocument xd=new XDocument( 
            new XElement("root", 
                    new XAttribute("color","re​​d"), 
                    new XAttribute("size","large"), 
                new XElement("最初")、
            ) 
        ); 
        Console.WriteLine(xd); 
        Console.WriteLine(); 
        XElement rt=xd.Element("ルート"); 
        XAttribute color=rt.Attribute("color"); 
        XAttribute size=rt.Attribute("size"); 
        Console.WriteLine("色は {0}",color.Value); 
        コンソール。WriteLine("サイズは {0}",size.Value); 
    } 
}

例: 属性を削除する

class Program 
{ 
    static void Main() 
    { 
        XDocument xd=new XDocument( 
            new XElement("root", 
                    new XAttribute("color","re​​d"), 
                    new XAttribute("size","large"), 
                new XElement("最初")、
            ) 
        ); 
        XElement rt=xd.Element("ルート"); 
        rt.Attribute("color").Remove();//移除color特性
        rt.SetAttributeValue("size",null);//移除サイズ特性
        Console.WriteLine(xd); 
    } 
}

例: 特性値の追加または変更

class Program 
{ 
    static void Main() 
    { 
        XDocument xd=new XDocument( 
            new XElement("root", 
                    new XAttribute("color","re​​d"), 
                    new XAttribute("size","large"), 
                new XElement("最初")、
            ) 
        ); 
        XElement rt=xd.Element("ルート"); 
        rt.SetAttributeValue("サイズ","中径"); //変更特性值
        rt.SetAttributeValue("width","narrow"); // 追加特性
        Console.WriteLine(xd); 
    } 
}

他のタイプのノード

Xコメント

XML コメントは、<!--和-->トークン間のテキストで構成されます。トークン間のテキストは XML パーサーによって無視されます。XComment クラスを使用して、XML ドキュメントにテキストを挿入できます。次のコードに示すように: 

 new XComment("これはコメントです")  

このコードは、次の XML ドキュメントを生成します。 
 <!--This is a comment--> 

X宣言

XML ドキュメントは、XML で使用されるバージョン番号、文字エンコードの種類、ドキュメントが外部参照に依存するかどうかを含む行で始まります。これは XML に関する情報なので、実際にはデータに関するメタデータです。これは XML 宣言と呼ばれ、XDeclaration クラスを使用して挿入できます。次のコードは XDeclaration の例を示します: 
 new XDeclaration("1.0","uff-8","yes")  
このコードは次の XML ドキュメントを生成します。 
 <?xml version="1.0" encoding="utf-8 " standalone="yes"?> 

X処理命令

XML 処理命令は、XML ドキュメントがどのように使用され、翻訳されるかに関する追加データを提供するために使用されます。最も一般的な処理命令は、XML ドキュメントをスタイル シートに関連付けるために使用されます。 
XProecssingstruction コンストラクターを使用して、処理命令を含めることができます。ターゲットとデータ文字列という 2 つの文字列パラメータを受け入れます。Ruge 処理命令は複数のデータ パラメーターを受け入れます。これらのデータ パラメーターは、次のコンストラクター コードに示すように、XProecssingstruction コンストラクターの 2 番目の文字列パラメーターに含める必要があります。

 new XProecssingstruct("xml-stylesheet",@"href=""stories"",type=""text/css""") 

このコードは、次の XML ドキュメントを生成します。 
 <?xml-stylesheet href="stories.css" type="text/css"?> 

例:

class Program 
{ 
    static void Main() 
    { 
        XDocument xd=new XDocument( 
            new XDeclaration("1.0","uff-8","yes"), 
            new XComment("これはコメントです"), 
            new XProecssingstruct("xml- stylesheet",@"href=""stories"",type=""text/css"""), 
            new XElement("root", 
                new XElement("first"), 
                new XElement("first") 
            ) 
        ); 
    } 
}

このコードにより、次の出力ファイルが生成されます。ただし、使用した場合WriteLine(xd)、宣言ステートメントは出力されません。 

LINQ to XMLを使用したLINQクエリ

これで、LINQ XML API と LINQ クエリ式を組み合わせて、シンプルかつ強力な XML ツリー検索を作成できるようになりました。

例: サンプル XML ツリーを作成する


            "大")) ) 
        ); 
        Console.WriteLine(xd);
        xd.Save("SimpleSample.xml"); 
    } 
}

例:LINQ to XML

class Program 
{ 
    static void Main() 
    { 
        XDocument xd=XDocument.Load("SimpleSample.xml"); 
        XElement rt=xd.Element("MyElements"); 
        var xyz=from e in rt.Elements() 
                where e.Name.ToString().Length==5 
                select e; 
        foreach(XElement x in xyz) 
        { 
            Console.WriteLine(x.Name.ToString()); 
        Console.WriteLine 
        (); 
        foreach(XElement x in xyz) 
        { 
            Console.WriteLine("名前: {0}、色: {1}、サイズ: {2}", 
                              x.Name, 
                              x.Attribute("color").Value,
                              x.Attribute("size").Value); 
        } 
} 
    }

例: XML ツリーのすべてのトップレベル要素を取得し、要素ごとに匿名型オブジェクトを作成します

システムを使用する; 
System.Linq を使用します。
System.Xml.Linq を使用します。
class Program 
{ 
    static void Main() 
    { 
        XDocument xd=XDocument.Load("SimpleSample.xml"); 
        XElement rt=xd.Element("MyElements"); 
        var xyz=from e in rt.Elements() 
                select new{e.Name,color=e.Attribute("color")}; 
                //创建匿名类型
        foreach(var x in xyz) 
        { 
            Console.WriteLine(x); 
        Console.WriteLine 
        (); 
        foreach(var x in xyz) 
        { 
            Console.WriteLine("{0,-6}, color:{1,-7}",x.Name,x.color.Value); 
        }
    } 
}

これらの例から、XML API と LIQN クエリ ツールを簡単に組み合わせて、強力な XML クエリ機能を生成できることがわかります。

 

著者: Moonache 
出典: http://www.cnblogs.com/moonache/ 
この記事の著作権は著者とブログパークに属します。転載は歓迎ですが、この声明は著者の同意なしに保持しなければなりません。原文は記事ページのわかりやすい位置に提供する必要があります。そうでない場合、当社は法的責任を追及する権利を留保します。

おすすめ

転載: blog.csdn.net/qq_24600981/article/details/81114413
おすすめ