[C# の簡単な説明] 第 9 章: C# の高度なトピック: LINQ クエリと式

C# の高度なトピックには、より複雑で柔軟なプログラミングの概念とテクニックが含まれており、開発者が最新のソフトウェア開発の課題にうまく対処できるようになります。その中でも、LINQ クエリと式は C# の高度なトピックの主要な内容であり、次の重要性と利点があります。

  1. データの処理と操作:最新のソフトウェアでは、データの処理と操作が非常に重要です。LINQ は、コレクション、データベース、XML など、さまざまな種類のデータをクエリおよび操作するための統一された方法を提供します。これにより、データ処理がより直観的かつ便利になります。
  2. 簡潔な構文: LINQ では SQL に似たクエリ構文が導入されており、開発者はデータ クエリと操作をより簡潔な方法で表現できるようになります。この直感的な構文により、コードの理解と保守が容易になります。
  3. 型安全性: LINQ はコンパイル時に型チェックされます。つまり、コンパイラはコンパイル時に型エラーを捕捉でき、実行時エラーの可能性を減らします。
  4. 生産性の向上: LINQ を使用すると、複雑なデータ クエリと変換操作を短時間で完了できるため、開発効率が向上します。開発者は、データを処理するために大量のループや一時変数を作成する必要がなくなりました。
  5. さまざまなデータ ソースに適用可能: LINQ はコレクション データだけでなく、データベース クエリや XML 処理などのさまざまなデータ ソースにも適用でき、さまざまな分野の開発に一貫したデータ処理方法を提供します。
  6. エラーの減少: LINQ は、開発者が範囲外、null 参照などの一般的なプログラミング エラーを回避するのに役立ちます。その構文とメソッドは、開発者がエッジケースをより適切に処理するのに役立ちます。
  7. 読みやすさ: LINQ のクエリ構文は非常に直観的であるため、コードの理解と保守が容易になります。これはチームワークとプロジェクトの維持に不可欠です。
  8. 複雑な要件に適応する:一部の複雑なデータ要件に対処する場合、LINQ の強力な機能は、開発者がより論理的なコードを記述し、ビジネス ロジックをより適切に表現するのに役立ちます。

1.LINQの概要

1.1 LINQ (Language Integrated Query) の定義と背景

LINQ (統合言語クエリ) は、データ クエリをプログラミング言語と統合するように設計された .NET Framework のプログラミング モデルです。その背景と定義は次のとおりです。
背景:以前、開発者は、さまざまな種類のデータに対するクエリや操作にさまざまな構文と API を使用する必要がありました。たとえば、リレーショナル データベースではクエリに SQL を使用する必要がありますが、.NET ではコレクションや XML などを操作するためにさまざまな API を使用する必要があります。この場合、コードは断片化して保守が困難になり、複数のクエリ言語を学習する必要があります。
定義: LINQ は上記の問題を解決し、プログラミング言語でクエリを統合する方法です。LINQ を使用すると、開発者は、基礎となるデータ ソースの種類やクエリ メソッドを知らなくても、統一された構文を使用して .NET 言語 (C# など) でクエリ操作を実行できます。コレクション、データベース、XML、その他のデータ ソースのいずれであっても、同様の構文を使用してクエリと操作を行うことができます。
LINQ の主な目標は、統一されたクエリ エクスペリエンスを提供し、開発者がプロ​​グラミング言語でより直観的かつ柔軟な方法でデータを処理できるようにすることです。これにより、開発効率が向上するだけでなく、コードがより読みやすく、保守しやすくなります。同時に、LINQ はコンパイル時に型チェックされるため、実行時エラーも減らすことができます。

1.2 LINQ の機能と用途

LINQ (言語統合クエリ) には次の特徴と用途があります。

  1. 統一された構文: LINQ は統一されたクエリ構文を提供します。コレクション、データベース、XML、その他のデータ ソースのいずれに対してクエリを実行する場合でも、同様の構文を使用してクエリと操作を実行できるため、学習コストとコードの複雑さが軽減されます。
  2. コンパイル時の型チェック: LINQ はコンパイル時に型チェックを実行します。つまり、コード作成段階でエラーを見つけることができ、実行時エラーが発生する可能性が低くなります。
  3. 遅延読み込み: LINQ は遅延読み込み (遅延実行) メカニズムを使用し、実際のクエリはクエリ結果が必要な場合にのみ実行されるため、パフォーマンスが最適化されます。
  4. 強力なクエリ機能: LINQ は、フィルタリング、並べ替え、射影、グループ化などのさまざまなクエリ操作を実行できる豊富なクエリ演算子とメソッドを提供します。
  5. オブジェクト指向クエリ: LINQ はオブジェクト指向であり、リレーショナル データベースに限定されず、オブジェクトをクエリできます。
  6. クエリとコードの融合: LINQ クエリ式とコードが混合して記述されるため、クエリとビジネス ロジックが統合され、コードの可読性が向上します。
  7. 幅広いアプリケーション: LINQ は、リレーショナル データベースだけでなく、コレクション、XML、オブジェクトなどのさまざまなデータ ソースにも適しています。
  8. 統合: LINQ は .NET 言語 (C# など) と緊密に統合されているため、新しいクエリ言語を追加で学習する必要はありません。
  9. 拡張機能のサポート:カスタム拡張メソッドを使用して、カスタム クエリ操作を LINQ に追加できます。

LINQ の用途には以下が含まれますが、これらに限定されません。

  • データベース クエリ: 従来の SQL クエリの代わりに、LINQ を使用してリレーショナル データベースをクエリできます。
  • コレクション操作: 従来のループ走査に代わって、コレクションに対するフィルター、並べ替え、グループ化などの操作を行うことができます。
  • XML 処理: XML ドキュメントは LINQ を通じてクエリおよび操作できるため、XML 処理がより簡潔になります。
  • オブジェクト クエリ: ビジネス ロジック処理のためにオブジェクト コレクションをクエリできます。
  • データ変換: データベースの結果をオブジェクトのコレクションに変換するなど、ある形式のデータを別の形式に変換することができます。
1.3 LINQ クエリと式の基本的な動作原理

LINQ (統合言語クエリ) クエリと式の基本的な動作原理は次のとおりです。

  1. クエリ式の変換: LINQ クエリ構文を使用すると、コンパイラはこれらのクエリ式を標準の拡張メソッド呼び出しに変換します。System.Linqこれらの拡張メソッドは、名前空間にあるLINQ 標準クエリ演算子のセットに属しています。
  2. 遅延実行: LINQ クエリは遅延実行の概念を採用しています。つまり、クエリ式はクエリ操作をすぐに実行せず、クエリ結果が実際に必要になったときに実行されます。データ ソースは必要な場合にのみアクセスされるため、これによりパフォーマンスが向上します。
  3. クエリ変換:クエリが実行されると、LINQ プロバイダーは LINQ クエリを特定のデータ ソース (コレクション、データベース、XML など) のクエリ言語に変換します。これは、LINQ クエリの構文がどのような場合でも一貫していることを意味します。データソースとは何か。
  4. 最適化と改善: LINQ プロバイダーは、クエリのパフォーマンスを向上させるためにクエリを最適化しようとします。これには、データベース クエリでの最適な SQL クエリ ステートメントの生成だけでなく、フィルタリングおよび射影操作の最適化も含まれる場合があります。
  5. コンパイル時の型チェック: LINQ クエリはコンパイル時に型チェックされるため、コンパイル時のエラーの検出と実行時の型エラーの回避に役立ちます。
  6. 結果を返す:最終的に、LINQ クエリは結果セットを返します。結果セットは、クエリの目的とデータ ソースに応じて、コレクション、単一の値、またはその他のものになります。

2、LINQの基本

2.1 LINQ クエリの構文と構造
  1. クエリ式の構文:
    クエリ式は、SQL に似た構文を使用してクエリを作成します。一般的なクエリ式のキーワードと例をいくつか示します。
  • from : データ ソースとスコープ変数を指定します。
  • ここで、 : データをフィルタリングするために使用されます。
  • orderby : データの並べ替えに使用されます。
  • select : データを投影するために使用され、返されるデータの部分を選択します。
  • group : データをグループ化するために使用されます。
  • join : 2 つのデータ ソースを結合するために使用されます。
  • into : あるクエリの結果を別のクエリに導入するために使用されます。

次に、クエリ式構文を使用して整数のリストから偶数を選択し、昇順に並べ替える例を示します。

var numbers = new List<int> {
    
     1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };

var query = from num in numbers
            where num % 2 == 0
            orderby num ascending
            select num;

foreach (var num in query)
{
    
    
    Console.WriteLine(num);
}
  1. メソッド構文:
    メソッド構文では、拡張メソッドを使用してクエリを実装します。これはよりコンパクトで、メソッド チェーン内で複数の操作を連続して呼び出すことができます。一般的な LINQ メソッドをいくつか示します。
  • ここで: データをフィルタリングするために使用されます。
  • OrderBy / OrderByDescending : データの並べ替えに使用されます。
  • 選択: データを投影するために使用されます。
  • GroupBy : データをグループ化するために使用されます。
  • Join : 2 つのデータ ソースを接続するために使用されます。
  • ToList / ToArray : クエリ結果をリストまたは配列に変換します。
  • Count : 要素の数を返します。
  • First / FirstOrDefault : 最初の要素を返します。
  • Single / SingleOrDefault : 単一の要素を返します。
  • Aggregate : シーケンス内の要素に対して累積演算を実行します。

以下は、メソッド構文を使用して文字列のリストから 3 を超える長さの文字列を選択し、長さの昇順で並べ替える例です。

var strings = new List<string> {
    
     "apple", "banana", "grape", "orange", "kiwi" };

var query = strings
    .Where(str => str.Length > 3)
    .OrderBy(str => str.Length)
    .ToList();

foreach (var str in query)
{
    
    
    Console.WriteLine(str);
}

クエリ式とメソッド構文はどちらも、.NET アプリケーションでのデータ クエリと操作のための LINQ を記述する 2 つの異なる方法です。どの構文を選択するかは、主に個人的な好みと特定の使用シナリオによって決まります。

2.2 クエリ構文とメソッド構文の比較

LINQ は、データのクエリと操作を実行するために、クエリ式構文とメソッド構文という 2 つの異なる構文スタイルを提供します。以下に 2 つの構文スタイルの比較を示します。
クエリ式の構文:

  • SQL に似た構文を使用し、自然言語に近づけます。
  • 初心者向けにさらに読みやすく、理解しやすくなりました。
  • 通常、複数の条件、並べ替え、グループ化を含む複雑なクエリに適しています。
  • クエリのコードは長くなり、複数の操作を 1 つのステートメントに組み合わせることができます。
  • サポートされる操作は限られていますが、一般的なクエリ要件には十分です。
  • 特に複雑なクエリの場合、コードの読みやすさをある程度向上させることができます。
var query = from person in people
            where person.Age > 18
            orderby person.LastName
            select person.FirstName;

メソッドの構文:

  • 連鎖呼び出しを使用した拡張メソッドは、よりコンパクトで簡潔です。
  • 単純なクエリ、特にフィルタリング、並べ替え、射影などの基本的な操作のみを含むクエリに適しています。
  • コード内で操作をよりきめ細かく制御できるため、柔軟な操作シーケンスに適しています。
  • 操作はより柔軟であり、要件に応じてさまざまなメソッド呼び出しを組み合わせることができます。
  • LINQ 拡張メソッドを呼び出すことができるため、サポートされる操作が豊富になります。
  • より高度なコード制御とパフォーマンスの最適化が必要な状況を対象としています。
var query = people
    .Where(person => person.Age > 18)
    .OrderBy(person => person.LastName)
    .Select(person => person.FirstName);

クエリ式構文は、より可読性が必要なクエリに適しており、メソッド構文は、より柔軟性とパフォーマンスの最適化が必要な状況に適しています。実際の開発では、状況に応じて最適な文法を選択できます。同時に、2 つの構文は同等であり、相互に変換できるため、特定のニーズに応じて 2 つのスタイルを切り替えることもできます。

2.3 基本的な LINQ クエリ演算子の概要

LINQ は、さまざまなデータ ソース (コレクション、データベース、XML など) からのデータ クエリと操作のための一連の基本的なクエリ演算子を提供します。これらの演算子を使用すると、フィルター、並べ替え、プロジェクト、グループ化などが可能になります。次に、一般的に使用される基本的な LINQ クエリ演算子のいくつかを紹介します。

  1. Where : 指定された基準に基づいて要素をフィルタリングするために使用されます。条件を満たす要素のみを返します。
var result = collection.Where(item => item.Property > 5);
  1. OrderBy / OrderByDescending : 要素を昇順または降順で並べ替えるのに使用されます。
var result = collection.OrderBy(item => item.Property);
  1. 選択: データの投影、要素の特定の属性の選択、または変換操作の実行に使用されます。
var result = collection.Select(item => item.Property);
  1. GroupBy : 要素をグループ化するために使用され、通常は集計関数とともに使用されます。
var result = collection.GroupBy(item => item.Category);
  1. Join : 2 つのデータ ソース内の要素を接続し、共通キーに従って接続するために使用されます。
var result = collection1.Join(collection2, item1 => item1.Key, item2 => item2.Key, (item1, item2) => new {
    
     item1, item2 });
  1. Distinct : 重複した要素を削除するために使用されます。
var result = collection.Distinct();
  1. Take / Skip : 最初の N 要素を取得するか、シーケンスから最初の N 要素をスキップするために使用されます。
var result = collection.Take(5);
var result = collection.Skip(3);
  1. First / FirstOrDefault : シーケンス内の最初の要素を取得するために使用されます。
var result = collection.First();
var result = collection.FirstOrDefault();
  1. Single / SingleOrDefault : シーケンス内の単一の要素を取得するために使用されます。
var result = collection.Single(item => item.Property == value);
var result = collection.SingleOrDefault(item => item.Property == value);
  1. Any : シーケンス内に条件を満たす要素があるかどうかを確認するために使用されます。
bool hasItems = collection.Any(item => item.Property > 5);
  1. All : シーケンス内のすべての要素が条件を満たすかどうかを確認するために使用されます。
bool allMatch = collection.All(item => item.Property > 0);
  1. Count : 条件を満たすシーケンス内の要素の数を返します。
int count = collection.Count(item => item.Property > 5);
  1. Sum / Average : シーケンス内の要素の合計または平均を計算するために使用されます。
var sum = collection.Sum(item => item.Value);
var average = collection.Average(item => item.Value);

3. LINQクエリのデータソース

3.1 LINQ クエリのデータ ソースの種類
  1. IEnumerable<T> : これは最も一般的なデータ ソースの種類であり、配列、リスト、セットなどの列挙可能なコレクションを表します。
  2. IQueryable<T> : これはクエリ可能なデータ ソースを表し、通常はデータベース クエリと対話するために使用されます。遅延読み込みをサポートしています。つまり、クエリはすぐには実行されませんが、結果が必要なときに実行されるため、クエリのパフォーマンスが最適化されます。
  3. Array : C# の配列は、LINQ クエリで直接使用できます。
  4. List<T> : List は、LINQ クエリにも使用できる一般的なコレクション型です。
  5. Dictionary<TKey, TValue> : キーによるクエリに辞書を使用できます。
  6. DataSet / DataTable : これらはデータベース内の表形式データを処理するために使用され、フィルタリング、並べ替え、射影などの操作を LINQ クエリを通じて実行できます。
  7. XML : LINQ to XML を使用すると、LINQ クエリと同様の方法で XML データを処理できます。
  8. Entity Framework : Entity Framework は、データベース内のテーブルを .NET オブジェクトにマップできる ORM (オブジェクト リレーショナル マッピング) ツールであり、データベースを操作するための LINQ クエリの使用をサポートします。
  9. LINQ to SQL : データベース内で LINQ クエリを実行するための Entity Framework に似ています。
  10. LINQ to Objects : これは、.NET のオブジェクトのコレクション用の標準 LINQ プロバイダーであり、さまざまなコレクション型で利用できます。
  11. LINQ to Entities : エンティティ データ モデルをデータベースに接続するために使用され、データベース操作での LINQ クエリの使用をサポートします。
  12. LINQ to XML : XML データの処理に使用され、LINQ モードでの XML ドキュメントのクエリと操作をサポートします。
  13. LINQ to JSON : JSON データの処理に使用され、LINQ モードでの JSON データのクエリと操作をサポートします。
  14. Parallel LINQ (PLINQ) : これは並列実行をサポートする LINQ 拡張機能であり、マルチコア プロセッサでのクエリの実行に適しています。
3.2 LINQ クエリ用のデータ ソースを作成および準備する方法

LINQ クエリ用のデータ ソースの作成と準備には、さまざまなデータ型からデータを取得し、それらを IEnumerable、IQueryable などの LINQ に適したデータ型に変換することが含まれます。LINQ クエリ用のデータ ソースを作成および準備する一般的な方法をいくつか示します。

  1. コレクション型を使用します

    • 配列を使用します。T[] array = new T[] { ... };
    • List<T> を使用します。List<T> list = new List<T> { ... };
    • 使用Dictionary<TKey, TValue>:Dictionary<TKey, TValue> dict = new Dictionary<TKey, TValue> { ... };
  2. LINQ によって提供されるデータ ソースを使用します

    • Enumerable.Range : 一連の整数範囲を作成します。
      var numbers = Enumerable.Range(1, 10); // 创建从1到10的整数序列
      
    • Enumerable.Repeat : 繰り返し値のシーケンスを作成します。
      var repeatedValues = Enumerable.Repeat("Hello", 5); // 创建包含5个"Hello"的序列
      
  3. LINQ to XML の使用: LINQ クエリを使用して XML ドキュメントからデータを抽出します。

    XDocument xmlDocument = XDocument.Load("data.xml");
    var data = from element in xmlDocument.Root.Elements("item")
               select new {
          
          
                   Name = element.Element("name").Value,
                   Price = decimal.Parse(element.Element("price").Value)
               };
    
  4. LINQ to Entities / LINQ to SQL の使用: Entity Framework や LINQ to SQL などの ORM ツールを使用して、データベースからデータをフェッチします。

    var dbContext = new MyDbContext();
    var data = from product in dbContext.Products
               where product.Category == "Electronics"
               select product;
    
  5. ファイルまたは外部データ ソースからデータを読み取ります

    • テキスト ファイルからデータを読み取る: File クラスを使用して、テキスト ファイルからデータを読み取ります。
    • CSV ファイルからデータを読み取る: CsvHelper などのオープン ソース ライブラリを使用して、CSV ファイルからデータをオブジェクトに変換します。
    • データベースからデータを読み取る: ADO.NET または ORM ツールを使用して、データベース内のデータを取得します。
  6. カスタム データ ソースの作成: 独自のコレクション クラスまたはデータ プロバイダーを実装して、LINQ クエリをサポートできます。

4、LINQ クエリの操作と結果

4.1 複数の LINQ クエリ演算子を作成して組み合わせる方法

複数の LINQ クエリ演算子の構築と結合は、演算子呼び出しを連鎖することによって実現されます。単一の LINQ クエリで複数の演算子を使用して、データに対して複雑なクエリ、フィルター、射影、操作を実行できます。複数の LINQ クエリ演算子を構築して組み合わせる方法の例を次に示します。
それぞれに名前、年齢、職業の属性を持つ、人々に関するいくつかの情報を含むコレクションがあるとします。このコレクションから 18 歳以上の人々を選択し、年齢の昇順に並べ替えて、名前と職業情報のみを選択したいと考えています。

class Person
{
    
    
    public string Name {
    
     get; set; }
    public int Age {
    
     get; set; }
    public string Occupation {
    
     get; set; }
}

// 假设已经有一个包含人员信息的集合 people
List<Person> people = new List<Person>
{
    
    
    new Person {
    
     Name = "Alice", Age = 25, Occupation = "Engineer" },
    new Person {
    
     Name = "Bob", Age = 30, Occupation = "Teacher" },
    new Person {
    
     Name = "Charlie", Age = 22, Occupation = "Student" }
};

// 构建并组合多个操作符
var query = people
    .Where(person => person.Age > 18)  // 筛选年龄大于18的人员
    .OrderBy(person => person.Age)      // 按照年龄升序排列
    .Select(person => new              // 选择姓名和职业信息
    {
    
    
        person.Name,
        person.Occupation
    });

// 执行查询并遍历结果
foreach (var personInfo in query)
{
    
    
    Console.WriteLine($"Name: {
      
      personInfo.Name}, Occupation: {
      
      personInfo.Occupation}");
}

上の例では、およびWhere演算子を使用してクエリ チェーンを構築しました。各演算子は新しいクエリ オブジェクトを返すため、そのオブジェクトに対して他の演算子を引き続き呼び出すことができることに注意してください。最後に、クエリ結果をループして出力します。OrderBySelectforeach

4.2 クエリ演算子の戻り値の型と結果の処理

LINQ クエリ演算子によって返される型は、演算子自体と、操作前のデータ ソースの種類によって異なります。演算子が異なれば、異なるタイプのシーケンスまたは個々の要素が返される場合があります。以下に、いくつかの一般的な LINQ クエリ演算子の戻りの型とクエリ結果の処理方法を示します。

  1. Where : 条件に一致する要素のシーケンスを返します。
    IEnumerable<TSource> result = collection.Where(item => item.Property > 5);
    
  2. OrderBy / OrderByDescending : 指定された順序で並べ替えられた要素のシーケンスを返します。
    IOrderedEnumerable<TSource> result = collection.OrderBy(item => item.Property);
    
  3. Select : 指定された投影を含むシーケンスを返します。
    IEnumerable<TResult> result = collection.Select(item => item.Property);
    
  4. GroupBy : グループ化されたシーケンスを返します。各グループにはキーと対応する要素シーケンスが含まれます。
    IEnumerable<IGrouping<TKey, TSource>> result = collection.GroupBy(item => item.Category);
    
  5. Join : 各要素が両方のデータ ソースに一致する結合されたシーケンスを返します。
    IEnumerable<TResult> result = collection1.Join(collection2, item1 => item1.Key, item2 => item2.Key, (item1, item2) => new {
          
           item1, item2 });
    
  6. Distinct : 重複排除された要素のシーケンスを返します。
    IEnumerable<TSource> result = collection.Distinct();
    
  7. Take / Skip : 指定された数の最初の N 要素以降、または最初の N 要素をスキップした後の要素のシーケンスを返します。
    IEnumerable<TSource> result = collection.Take(5);
    IEnumerable<TSource> result = collection.Skip(3);
    
  8. First / FirstOrDefault : シーケンス内の最初の要素を返します。
    TSource result = collection.First();
    TSource result = collection.FirstOrDefault();
    
  9. Single / SingleOrDefault : シーケンス内の単一の要素を返します。
    TSource result = collection.Single(item => item.Property == value);
    TSource result = collection.SingleOrDefault(item => item.Property == value);
    
  10. Any : シーケンスに条件を満たす要素が含まれているかどうかを示すブール値を返します。
    bool result = collection.Any(item => item.Property > 5);
    
  11. Count : 条件を満たす要素の数を返します。
    int count = collection.Count(item => item.Property > 5);
    
  12. Sum / Average : シーケンス内の要素の合計または平均を返します。
    decimal sum = collection.Sum(item => item.Value);
    double average = collection.Average(item => item.Value);
    

クエリ演算子の戻り値の型に応じて、クエリ結果を処理するさまざまな方法を選択できます。

  • シーケンスを返す演算子の場合、ループ (例: foreach) を使用して結果を反復し、各要素を処理できます。
  • 単一の要素を返す演算子の場合、結果を変数に保存してさらに処理できます。
  • 結果をリストまたは配列に変換する必要がある場合は、ToList()またはToArray()メソッドを使用できます。
4.3 遅延実行と即時実行の違い

LINQ では、クエリ操作は遅延実行と即時実行に分類できます。2 つの実行方法の主な違いは、クエリがいつ実行されるか、および返される結果のタイプです。

  1. 遅延実行 (Deferred Execution) :
    遅延実行とは、クエリがすぐには実行されず、クエリ結果が実際に必要になったときに実行されることを意味します。これにより、複雑なクエリ チェーンを構築し、必要な場合にのみ実行できます。遅延実行の特徴は次のとおりです。
  • クエリの定義と構成はクエリ チェーンの作成時に行われますが、クエリ自体は実行されません。
  • クエリの実行は、ループ内でクエリ結果をトラバースするときや端末オペレータ (たとえば、ToList()などToArray()) を呼び出すときなど、クエリ結果にアクセスするまで延期されます。
  • クエリが実行されると、クエリ チェーンの作成時のデータ ソースではなく、最新のデータ ソースが計算に使用されます。
  • 大量のデータを走査する際のパフォーマンスを最適化するために使用でき、必要なデータのみを計算して返します。
var query = collection.Where(item => item.Property > 5); // 定义查询
foreach (var item in query) // 在循环中执行查询
{
    
    
    Console.WriteLine(item);
}
  1. 即時実行:
    即時実行とは、クエリが定義されるとすぐに実行され、クエリ結果が返されることを意味します。これは、クエリの計算がコードの後半ではなく、すぐに実行されることを意味します。
  • クエリの結果は、クエリ式ではなく実際のデータです。
  • クエリ演算子が即座に呼び出され、データが取得および処理され、結果が変数に返されます。
  • クエリ結果を取得してすぐにデータを処理したい場合に適用されます。
var result = collection.Where(item => item.Property > 5).ToList(); // 立即执行查询并获取结果

どの実行方法が使用されているかを理解するには、特定の演算子の定義とクエリ チェーン内のその位置を確認する必要があります。一般に、使用される演算子のタイプと終端演算子 ( ToList()ToArray()First()など) によって、クエリの実行方法が決まります。遅延実行と即時実行の違いを理解すると、クエリのパフォーマンスを最適化し、不要な計算を回避するのに役立ちます。

5、LINQ と匿名型

5.1 匿名型を使用したクエリ結果の処理

LINQ では、匿名型は、クエリ結果データの一部またはすべてを格納するために使用される一時的なクエリ専用型です。匿名型を使用すると、クラスを明示的に定義せずに返すプロパティを選択する便利な方法が提供されます。匿名型を使用してクエリ結果を処理する方法の例を次に示します。
それぞれに名前、年齢、職業の属性を持つ、人々に関する情報が含まれるコレクションがあるとします。このコレクションから 18 歳以上の人を選択し、名前と職業情報を返したいと考えています。

class Person
{
    
    
    public string Name {
    
     get; set; }
    public int Age {
    
     get; set; }
    public string Occupation {
    
     get; set; }
}

List<Person> people = new List<Person>
{
    
    
    new Person {
    
     Name = "Alice", Age = 25, Occupation = "Engineer" },
    new Person {
    
     Name = "Bob", Age = 30, Occupation = "Teacher" },
    new Person {
    
     Name = "Charlie", Age = 22, Occupation = "Student" }
};

// 使用匿名类型选择要返回的属性
var query = from person in people
            where person.Age > 18
            select new {
    
     person.Name, person.Occupation };

foreach (var personInfo in query)
{
    
    
    Console.WriteLine($"Name: {
      
      personInfo.Name}, Occupation: {
      
      personInfo.Occupation}");
}

上の例では、select new { ... }ステートメントを使用して匿名型を作成し、返すプロパティのみを選択しました。匿名型のプロパティ名は、クエリ結果のプロパティ名から推測されます。次に、foreachクエリ結果をループして出力します。
次の重要な点に注意してください。

  • 匿名型の型名はコンパイラによって生成され、コンパイル時には表示されません。
  • 匿名型のインスタンスを作成するたびに、クエリ結果のプロパティ名と型と一致するプロパティ名と型を持つ新しいクラスを実際に作成することになります。
  • 匿名型は一時的なものであるため、クエリのスコープ内でのみ使用でき、メソッドの外に渡すことはできません。
  • 匿名型のプロパティは読み取り専用であり、その値は変更できません。

ヒント: 匿名型を使用するとコードがより簡潔になり、クラスを明示的に定義する必要がないため、クエリ結果の一時的な処理に適しています。

6 つ、LINQ およびコレクション型

6.1 LINQ クエリでコレクション型を処理する方法

LINQ の主な目的の 1 つはコレクションのクエリ、フィルター、投影、および操作であるため、LINQ クエリでコレクション型を操作することは非常に一般的です。LINQ クエリでコレクション型を操作する一般的な例をいくつか示します。

  1. データのフィルター (Where) :演算子を
    使用してコレクション内の要素をフィルターし、条件を満たす要素のみを保持します。Where
var result = collection.Where(item => item.Property > 5);
  1. データの並べ替え (OrderBy、OrderByDescending) : or演算子
    を使用して、コレクション要素を昇順または降順で並べ替えます。OrderByOrderByDescending
var result = collection.OrderBy(item => item.Property);
  1. 投影データ (選択) :演算子
    を使用して、Selectコレクションから特定の属性を選択するか、変換操作を実行します。
var result = collection.Select(item => item.Property);
  1. データのグループ化 (GroupBy) :演算子
    を使用して、特定の属性ごとにコレクション要素をグループ化します。GroupBy
var result = collection.GroupBy(item => item.Category);
  1. データの結合 (Join) :演算子を
    使用して、共通キーに従って 2 つのコレクション内の要素を接続します。Join
var result = collection1.Join(collection2, item1 => item1.Key, item2 => item2.Key, (item1, item2) => new {
    
     item1, item2 });
  1. Distinct :演算子
    を使用して、Distinctコレクション内の重複要素を削除します。
var result = collection.Distinct();
  1. 最初の N 要素を取得 (Take) :演算子
    を使用して、コレクション内の最初の N 要素を取得します。Take
var result = collection.Take(5);
  1. 最初の N 要素をスキップ (Skip) :演算子
    を使用して、コレクション内の最初の N 要素をスキップします。Skip
var result = collection.Skip(3);
6.2 LINQ を使用したコレクションのフィルタリング、マッピング、並べ替え

LINQ を使用したコレクションのフィルタリング、マッピング、並べ替えは非常に簡単で、LINQ の対応する演算子を使用するだけです。以下に、LINQ を使用して学生情報を含むコレクションをフィルター、マップ、並べ替える方法の例を示します。

class Student
{
    
    
    public string Name {
    
     get; set; }
    public int Age {
    
     get; set; }
    public double GPA {
    
     get; set; }
}

List<Student> students = new List<Student>
{
    
    
    new Student {
    
     Name = "Alice", Age = 20, GPA = 3.5 },
    new Student {
    
     Name = "Bob", Age = 22, GPA = 3.2 },
    new Student {
    
     Name = "Charlie", Age = 19, GPA = 3.8 }
};

// 过滤:选择年龄小于 22 的学生
var filteredStudents = students.Where(student => student.Age < 22);

// 映射:仅选择学生的姓名和GPA信息
var mappedStudents = students.Select(student => new {
    
     student.Name, student.GPA });

// 排序:按照GPA降序排列学生
var sortedStudents = students.OrderByDescending(student => student.GPA);

// 输出过滤后的学生信息
Console.WriteLine("Filtered Students:");
foreach (var student in filteredStudents)
{
    
    
    Console.WriteLine($"Name: {
      
      student.Name}, Age: {
      
      student.Age}, GPA: {
      
      student.GPA}");
}

// 输出映射后的学生信息
Console.WriteLine("Mapped Students:");
foreach (var student in mappedStudents)
{
    
    
    Console.WriteLine($"Name: {
      
      student.Name}, GPA: {
      
      student.GPA}");
}

// 输出排序后的学生信息
Console.WriteLine("Sorted Students:");
foreach (var student in sortedStudents)
{
    
    
    Console.WriteLine($"Name: {
      
      student.Name}, Age: {
      
      student.Age}, GPA: {
      
      student.GPA}");
}

上の例では、フィルタリング、マッピング、並べ替えの操作にそれぞれおよびWhere演算子を使用しましたこれらの演算子を使用すると、コレクションを簡潔な方法で操作して、ニーズに合った結果を得ることができます。これらの演算子は新しいクエリ オブジェクトを返すため、元のコレクションは変更されないままであることに注意してください。SelectOrderByDescending

セブン、LINQ、データベース

7.1 LINQ を使用したデータベース クエリ

LINQ を使用してデータベースをクエリするには、通常、Entity Framework などの ORM (オブジェクト リレーショナル マッピング) ツールを使用します。これを使用すると、データベース内のテーブルを .NET オブジェクトにマップし、LINQ を使用してクエリ操作を実行できます。以下は、Entity Framework を使用してデータベース クエリを実行するときの基本的な例です。学生の名前、年齢、学年の情報を含む
データベース テーブルがあるとします。Studentsデータベースから 22 歳未満の学生を選択し、学年ごとに降順に並べ替えたいと考えています。

using System;
using System.Linq;

// 引入Entity Framework相关命名空间
using Microsoft.EntityFrameworkCore;

// 定义与数据库表对应的实体类
public class Student
{
    
    
    public int Id {
    
     get; set; }
    public string Name {
    
     get; set; }
    public int Age {
    
     get; set; }
    public double GPA {
    
     get; set; }
}

// DbContext 表示数据库上下文
public class SchoolDbContext : DbContext
{
    
    
    public DbSet<Student> Students {
    
     get; set; }
    // 其他DbSet和配置可以在这里添加
}

class Program
{
    
    
    static void Main()
    {
    
    
        using (var dbContext = new SchoolDbContext())
        {
    
    
            // 过滤并排序:选择年龄小于 22 的学生,按成绩降序排列
            var query = dbContext.Students
                .Where(student => student.Age < 22)
                .OrderByDescending(student => student.GPA);

            // 执行查询
            foreach (var student in query)
            {
    
    
                Console.WriteLine($"Name: {
      
      student.Name}, Age: {
      
      student.Age}, GPA: {
      
      student.GPA}");
            }
        }
    }
}

上の例では:

  1. Studentデータベース テーブルにマップするクラスを定義しますStudents
  2. を継承するクラスを作成しました。このクラスにはDbContext学生データ テーブルを表すプロパティがSchoolDbContext含まれています。DbSet<Student>
  3. このメソッドではMain、インスタンスを作成しSchoolDbContext、LINQ を使用してStudentsコレクションを操作します。演算子を使用してWhere年齢が 22 歳未満の学生を除外し、 を使用してOrderByDescending成績の降順に並べ替えます。
  4. 最後に、query結果を反復処理して実際のクエリを実行し、結果を出力します。
7.1 Entity Framework と LINQ to SQL を使用したデータベース操作

C# プログラミング言語を使用する場合、データベース操作に Entity Framework と LINQ to SQL を使用できます。どちらのテクノロジもオブジェクト リレーショナル マッピング (ORM) のフレームワークであり、データベース操作をオブジェクト指向コードに変換することが容易になります。以下ではEntity FrameworkとLINQ to SQLそれぞれの基本的な使い方を紹介します。
Entity Framework:
Entity Framework は、複数のデータベース エンジンをサポートし、開発者がデータベース内のデータを .NET オブジェクトにマップするのに役立ち、LINQ クエリ言語のサポートを提供する強力な ORM フレームワークです。Entity Framework をデータベース操作に使用する方法を示す簡単な例を次に示します。

  1. Entity Framework をインストールする: NuGet パッケージ マネージャーを使用して、プロジェクトに Entity Framework パッケージをインストールします。
  2. エンティティ クラスの定義:データベース テーブルをマップするための C# クラスを定義します。
using System.ComponentModel.DataAnnotations;

public class Product
{
    
    
    [Key]
    public int ProductId {
    
     get; set; }
    public string Name {
    
     get; set; }
    public decimal Price {
    
     get; set; }
}
  1. DbContext クラスを作成する:DbContextデータベース コンテキストを定義するために継承するクラスを作成します。
using Microsoft.EntityFrameworkCore;

public class ApplicationDbContext : DbContext
{
    
    
    public DbSet<Product> Products {
    
     get; set; }
}
  1. データベース操作を実行する: DbContext を使用して、コード内でデータベース操作を実行します。
using System;
using System.Linq;

class Program
{
    
    
    static void Main(string[] args)
    {
    
    
        using (var context = new ApplicationDbContext())
        {
    
    
            var products = context.Products.Where(p => p.Price > 50).ToList();

            foreach (var product in products)
            {
    
    
                Console.WriteLine($"Product: {
      
      product.Name}, Price: {
      
      product.Price}");
            }
        }
    }
}

LINQ to SQL:
LINQ to SQL は、SQL Server データベースとの対話に重点を置いたデータベース操作のもう 1 つのテクノロジです。以下は、データベース操作に LINQ to SQL を使用する方法を示す簡単な例です。

  1. LINQ to SQL タイプを作成する: Visual Studio で LINQ to SQL タイプ (.dbml ファイル) を作成し、データベース テーブルをデザイン画面にドラッグ アンド ドロップします。
  2. データベース操作を実行します。
using System;
using System.Linq;

class Program
{
    
    
    static void Main(string[] args)
    {
    
    
        using (var db = new DataClassesDataContext())
        {
    
    
            var products = from p in db.Products
                           where p.Price > 50
                           select p;

            foreach (var product in products)
            {
    
    
                Console.WriteLine($"Product: {
      
      product.ProductName}, Price: {
      
      product.Price}");
            }
        }
    }
}

8. LINQ と XML

8.1 LINQ を使用した XML データのクエリと操作

C# では、LINQ を使用した XML データのクエリと操作が非常に便利です。LINQ to XML は、XML ドキュメントのクエリ、変更、作成を行うための簡潔な方法を提供します。以下は、LINQ to XML を使用して XML データをクエリおよび操作する方法を示すサンプル コードです。

using System;
using System.Linq;
using System.Xml.Linq;

class Program
{
    
    
    static void Main(string[] args)
    {
    
    
        // 加载 XML 文档
        string xmlData = @"
            <books>
                <book>
                    <title>Introduction to C#</title>
                    <author>John Smith</author>
                    <price>29.99</price>
                </book>
                <book>
                    <title>Advanced LINQ</title>
                    <author>Jane Doe</author>
                    <price>39.99</price>
                </book>
            </books>";

        XDocument doc = XDocument.Parse(xmlData);

        // 查询 XML 数据
        var query = from book in doc.Descendants("book")
                    where (double)book.Element("price") > 30.0
                    select new
                    {
    
    
                        Title = book.Element("title").Value,
                        Author = book.Element("author").Value,
                        Price = (double)book.Element("price")
                    };

        foreach (var book in query)
        {
    
    
            Console.WriteLine($"Title: {
      
      book.Title}, Author: {
      
      book.Author}, Price: {
      
      book.Price}");
        }

        // 修改 XML 数据
        XElement firstBook = doc.Descendants("book").First();
        firstBook.Element("price").Value = "34.99";

        // 添加新元素
        XElement newBook = new XElement("book",
            new XElement("title", "LINQ Unleashed"),
            new XElement("author", "Alice Johnson"),
            new XElement("price", "49.99"));
        doc.Root.Add(newBook);

        // 保存修改后的 XML 文档
        doc.Save("updated_books.xml");
    }
}

この例では、最初に XML 文字列をXDocumentオブジェクトとしてロードします。次に、LINQ クエリ構文を使用して、価格が 30.0 を超える書籍をフィルターします。次に、最初の本の価格を変更し、新しい本を追加しました。最後に、変更した XML ドキュメントを保存します。

8.2 LINQ to XML の基本的な使用法と構文

LINQ to XML は、XML データを操作するための C# のテクノロジであり、XML ドキュメントの作成、クエリ、および変更を行う便利な方法を提供します。以下に、LINQ to XML の基本的な使用法と構文の例を示します。
1. XML ドキュメントを作成します。

XDocument doc = new XDocument(
    new XElement("books",
        new XElement("book",
            new XElement("title", "Introduction to C#"),
            new XElement("author", "John Smith"),
            new XElement("price", "29.99")),
        new XElement("book",
            new XElement("title", "Advanced LINQ"),
            new XElement("author", "Jane Doe"),
            new XElement("price", "39.99"))
    )
);

2. XML データをクエリします。

var query = from book in doc.Descendants("book")
            where (double)book.Element("price") > 30.0
            select new
            {
    
    
                Title = book.Element("title").Value,
                Author = book.Element("author").Value,
                Price = (double)book.Element("price")
            };

foreach (var book in query)
{
    
    
    Console.WriteLine($"Title: {
      
      book.Title}, Author: {
      
      book.Author}, Price: {
      
      book.Price}");
}

3. XML データを変更します。

XElement firstBook = doc.Descendants("book").First();
firstBook.Element("price").Value = "34.99";

// 添加新元素
XElement newBook = new XElement("book",
    new XElement("title", "LINQ Unleashed"),
    new XElement("author", "Alice Johnson"),
    new XElement("price", "49.99"));
doc.Root.Add(newBook);

4. 変更した XML ドキュメントを保存します。

doc.Save("updated_books.xml");

LINQ to XML では、LINQ クエリと同様の構文を使用して XML データのクエリと変更を行うことができます。以下に、一般的に使用される LINQ to XML のメソッドとプロパティをいくつか示します。

  • XDocument: XML ドキュメント全体を表します。
  • XElement: XML要素を表します。
  • XAttribute: XML 属性を表します。
  • Descendants: 指定された名前のすべての子要素を取得します。
  • Elements: 指定された名前の直接の子要素を取得します。
  • Value: 要素の値を取得します。
  • Add: 新しい要素または属性を追加します。
  • Remove: 要素または属性を削除します。
  • Save: XML ドキュメントを保存します。

9、カスタム LINQ クエリ

9.1 拡張メソッドの作成と使用

C# では、LINQ 拡張メソッドを使用して、LINQ クエリの操作をカスタマイズできます。独自の LINQ 拡張メソッドを作成して、カスタム機能または操作を LINQ クエリに追加できます。LINQ 拡張メソッドを作成して使用する基本的な手順は次のとおりです。
LINQ 拡張メソッドの作成:

  1. 静的クラスを作成する: LINQ 拡張メソッドを含める静的クラスを作成します。
using System;
using System.Collections.Generic;

public static class MyLinqExtensions
{
    
    
    // 定义扩展方法
    public static IEnumerable<T> WhereGreaterThan<T>(this IEnumerable<T> source, T threshold)
        where T : IComparable<T>
    {
    
    
        foreach (var item in source)
        {
    
    
            if (item.CompareTo(threshold) > 0)
            {
    
    
                yield return item;
            }
        }
    }
}

上記のコードでは、 という静的クラスを作成しMyLinqExtensions、その中に拡張メソッドを定義しましたWhereGreaterThan
2.キーワードを使用しますthis。拡張メソッドの最初のパラメータの前にキーワードを追加しますthis。これは、メソッドが拡張メソッドであり、この型のインスタンスに対して動作することを示します。
LINQ 拡張メソッドを使用する場合:

using System;
using System.Collections.Generic;
using System.Linq;

class Program
{
    
    
    static void Main(string[] args)
    {
    
    
        List<int> numbers = new List<int> {
    
     1, 5, 9, 12, 15, 18, 20 };

        // 使用自定义的扩展方法
        var filteredNumbers = numbers.WhereGreaterThan(10);

        foreach (var num in filteredNumbers)
        {
    
    
            Console.WriteLine(num);
        }
    }
}

上の例では、型に対してList<int>カスタム拡張メソッドを使用しました。WhereGreaterThanこのメソッドは、指定されたしきい値より大きい要素をフィルターで除外します。

ヒント: 拡張メソッドは静的クラスで定義する必要があり、通常使用する前に名前空間を正しくインポートする必要があります。

9.2 カスタム LINQ クエリ演算子

C# では、カスタム LINQ クエリ演算子を作成して LINQ クエリ構文を拡張し、カスタム クエリ操作をサポートできます。以下に、カスタム LINQ クエリ演算子を作成して使用する方法を示す例を示します。
カスタム LINQ クエリ演算子の作成:

using System;
using System.Collections.Generic;

public static class MyLinqOperators
{
    
    
    public static IEnumerable<T> CustomFilter<T>(this IEnumerable<T> source, Func<T, bool> predicate)
    {
    
    
        foreach (var item in source)
        {
    
    
            if (predicate(item))
            {
    
    
                yield return item;
            }
        }
    }
}

上記のコードでは、MyLinqOperatorsという静的クラスを作成し、その中にカスタム クエリ演算子を定義しましたCustomFilterこの演算子は、指定された基準を満たす要素をフィルターで除外します。
カスタム LINQ クエリ演算子を使用します。

using System;
using System.Collections.Generic;
using System.Linq;

class Program
{
    
    
    static void Main(string[] args)
    {
    
    
        List<int> numbers = new List<int> {
    
     1, 5, 9, 12, 15, 18, 20 };

        // 使用自定义的查询操作符
        var filteredNumbers = numbers.CustomFilter(num => num > 10);

        foreach (var num in filteredNumbers)
        {
    
    
            Console.WriteLine(num);
        }
    }
}

上の例では、カスタムCustomFilterクエリ演算子を使用して 10 を超える要素を除外しました。

10、LINQ クエリのパフォーマンスと最適化

LINQ クエリのパフォーマンスの最適化は、特に大量のデータを扱う場合に重要なトピックです。LINQ は便利なクエリ構文を提供しますが、不適切に使用するとパフォーマンスの低下につながる可能性があります。LINQ クエリのパフォーマンスを最適化するためのいくつかの提案を次に示します。

  1. 適切なデータ ソースを選択する:List<T>、などIEnumerable<T>クエリのニーズに最も適したデータ ソースを選択しますIQueryable<T>IQueryable<T>効率を高めるために、データベース サーバーへのクエリを延期できます。
  2. 適切なクエリ演算子を使用する:問題に対して適切なクエリ演算子を選択し、不要な演算子を避けて不必要なオーバーヘッドを削減します。
  3. 遅延ロード:可能な限り遅延ロードを使用して、必要なデータのみをロードします。すべての結果が必要ない場合は、Take()メソッドとSkip()メソッドを使用して、返されるデータの量を制限できます。
  4. インデックス:データ ソースがインデックスをサポートしている場合は、データの取得を高速化するために、クエリでインデックス付きフィールドを必ず使用してください。
  5. インデックス付きフィールドを使用したフィルター:データベースが必要なデータをより迅速に見つけられるように、可能な場合はインデックス付きフィールドを使用してフィルターします。
  6. N+1 クエリの問題を回避する:リレーショナル データに関しては、Include()or projection ( Select()) を使用して N+1 クエリの問題を回避し、データベースの対話の数を減らします。
  7. 複数の操作を結合する:反復回数を減らすために、複数の操作を 1 つのクエリに結合してみてください。
  8. ループ内でのクエリの実行を避ける:繰り返しごとにクエリが 1 回実行されることを避けるために、クエリをループの外に移動します。
  9. ルックアップにインデックスまたはハッシュ テーブルを使用する:データを頻繁にルックアップする必要がある場合は、より高いクエリ パフォーマンスを得るためにインデックスまたはハッシュ テーブル データ構造の使用を検討できます。
  10. 適切なデータ キャッシュを使用する:頻繁に変更されないデータの場合は、クエリのパフォーマンスを向上させるためにキャッシュの使用を検討してください。
  11. 不必要なデータ変換を避ける:オーバーヘッドを軽減するために、クエリでのデータ型変換を頻繁に避けるようにしてください。
  12. 非同期操作を使用する:適切なシナリオでは、非同期クエリを使用すると同時実行パフォーマンスを向上させることができます。
  13. パフォーマンス テストと分析:パフォーマンス テスト ツールとアナライザーを使用して、クエリ パフォーマンスのボトルネックを評価し、最適化の機会を特定します。

11. まとめ

LINQ は、C# 開発者にデータのクエリと操作を行うための便利で柔軟な方法を提供し、コードの可読性と生産性を大幅に向上させる強力なテクノロジです。メモリ内のデータであっても、データベースや XML などのデータ ソースであっても、統一された構文を使用して操作できます。

おすすめ

転載: blog.csdn.net/gangzhucoll/article/details/132391087