気楽なソフト工房

プログラミングについていろいろな記事を書いています。



mykonos2008

Author:mykonos2008
システムエンジニアとして働いている30代の会社員です。
仕事や趣味でプログラムを書いている方の役に立つ記事を書いていきたいと思っています。
ご意見、ご感想はこちらまで
If you are an english speaker,Please visit my english blog.

上記の広告は1ヶ月以上更新のないブログに表示されています。
新しい記事を書く事で広告が消せます。
本日は、LINQ入門の5回目で、orderby句です。

早速、サンプルを紹介していきます。データソースは前回までと同じく、Personクラスの配列を使用します。

    class Program
    {
        private Person[] persons = null;

        Program()
        {
            //クエリのデータソースを初期化する
            persons = new Person[]
            {
                new Person(){ LastName="Tanaka",FirstName = "ichiro", Age = 20},
                new Person(){ LastName = "Sato",FirstName = "Kazuhiro",Age = 25},
                new Person(){ LastName = "Wada",FirstName = "Takashi",Age = 30},
                new Person(){ LastName = "Asada",FirstName = "Kenji",Age = 40},
                new Person(){ LastName = "Tanaka",FirstName = "Tatsuta",Age = 26},
                new Person(){ LastName = "Yoshino",FirstName = "Taro",Age = 27}
            };
        }
    }

まず、最初のサンプルは年齢の昇順で並び替えるサンプルです。

    void Sort()
    {
        IEnumerable<Person> query = from person in persons
                    orderby person.Age
                    select person;

        foreach (Person person in query)
        {
            Console.WriteLine("{0} {1} Age:{2}", person.LastName, person.FirstName, person.Age);
        }
    }

orderbyに続けて、範囲変数personのAge属性を指定します。SQLと同じように昇順、降順を指定することが出来ます。
昇順の場合は、範囲変数の後に、「ascending」を、降順の場合は、「descending」を指定します。
例) orderby person.Age descending

省略した場合は、ascendingを指定した場合と同じ動きになります。

orderbyはカンマで区切って複数指定することも出来ます。以下は、姓と名のぞれぞれ1文字目で並び替えるサンプルです。

   void SortComplex()
   {
       IEnumerable<Person> query = from person in persons
                                   orderby person.LastName.Substring(0, 1),person.FirstName.Substring(0, 1)
                                   select person;

       foreach (var person in query)
       {
           Console.WriteLine("{0} {1} Age:{2}", person.LastName, person.FirstName, person.Age);
       }
   }

結果は以下のように表示されます。

Asada Kenji Age:40
Sato Kazuhiro Age:25
Tanaka Ichiro Age:20
Tanaka Tatsuta Age:26
Wada Takashi Age:30
Yoshino Taro Age:27

where句と同様、並び替えをする属性を動的に変えたい場合が出てくると思います。
動的LINQを使う高度な方法もあるのですが、ここではこんな方法を考えてみました。
以下は、並べ替えに使用する属性を姓と名で切り替えるサンプルです。

まず、Personクラスにソート用のプロパティ「NameForOrder」を追加します。

    class Person
    {
        public String LastName {  get;  set; }
        public String FirstName { get; set; }
        public int Age { get; set; }

        public String NameForOrder { get; set; }
    }

そして、クエリを実行する前に、「NameForOrder」に姓か名のいずれかの値を設定します。
クエリではorderby句に「NameForOrder」を指定します。

   void SortByName(bool lastOrFirst)
   {
       //ソート用の属性に値を設定する
       foreach (Person person in persons)
       {
           //姓の場合
           if (lastOrFirst)
           {
               person.NameForOrder = person.LastName;
           }
           //名の場合
           else
           {
               person.NameForOrder = person.FirstName;
           }
       }

       IEnumerable<Person> query = from person in persons
                                   orderby person.NameForOrder
                                   select person;

       foreach (var person in query)
       {
           Console.WriteLine("{0} {1} Age:{2}", person.LastName, person.FirstName, person.Age);
       }
   }

ソート用のプロパティを持たないといけないので、
無駄もあるのですが、動的LINQを使うより簡単に、並び替えの属性を変更することができます。

次回はgroup句を説明します。

スポンサーサイト
さて、本日はwhere句を説明します。クエリの発行対象は前回の記事と同じく、Person型の配列を使用します。

    class Program
    {
        private Person[] persons = null;

        Program()
        {
            //クエリのデータソースを初期化する
            persons = new Person[]
            {
                new Person(){ LastName="Tanaka",FirstName = "ichiro", Age = 20},
                new Person(){ LastName = "Sato",FirstName = "Kazuhiro",Age = 25},
                new Person(){ LastName = "Wada",FirstName = "Takashi",Age = 30},
                new Person(){ LastName = "Asada",FirstName = "Kenji",Age = 40},
                new Person(){ LastName = "Tanaka",FirstName = "Tatsuta",Age = 26},
                new Person(){ LastName = "Yoshino",FirstName = "Taro",Age = 27}
            };
        }
    }

where句はデータソースから列挙するデータに対してフィルタをかけるための機能で、SQLのWhere句と同じ働きをします。
以下のサンプルを見てください。

   void SelectWhere()
   {
       var query = from person in persons
                   where person.Age >= 20 && person.Age < 30
                   select person;

       foreach (var person in query)
       {
           Console.WriteLine("{0}-{1}-{2}", person.LastName, person.FirstName, person.Age);
       }
   }

where句を使って年齢が20歳以上、30歳未満に該当する要素のみが列挙されるように記述しています。
条件を指定する際は、from句で指定した範囲変数「person」を使用しています。

where句を使う際には、条件に変数を用いるケースが多いと思います。
以下のようにクエリの中でも変数を利用することが出来ます。

   void SelectWhere(int fromVal,int toVal)
   {
       var query = from person in persons
                   where person.Age >= fromVal && person.Age < toVal
                   select person;

       foreach (var person in query)
       {
           Console.WriteLine("{0}-{1}-{2}", person.LastName, person.FirstName, person.Age);
       }
   }

where句には式の結果がブール値になるものであれば、何でも指定することが出来るので、
以下のようにメソッドを呼んでもOKです。

   void SelectWhereName(String name)
   {
       var query = from person in persons
                   where person.LastName.StartsWith(name)
                   select person;

       foreach (var person in query)
       {
           Console.WriteLine("{0}-{1}-{2}", person.LastName, person.FirstName, person.Age);
       }
   }

実際のアプリケーション開発においてはクエリを動的に組み立てるケースが多いと思います。
例えば、姓と名のそれぞれが条件として指定されれば、それを検索条件とし、指定されなければ、条件としない
といった場合、SQLだとWhere句を動的に生成するようなコードを記述することになると思います。

LINQでも式ツリーを使用することで動的にクエリを構築することが出来ますが、
これは少し難解なので、もう少し後の記事で題材にします。

今回は代替手段としてこんな方法を考えてみました。

   void SelectWihthName(String lastName, String firstName)
   {
       var query = from person in persons
                   where MatchesName(person, lastName, firstName)
                   select person;

       foreach (Person person in query)
       {
           Console.WriteLine("{0} {1} Age:{2}", person.LastName, person.FirstName, person.Age);
       }
   }

   bool MatchesName(Person person,String lastName, String firstName)
   {
       //姓が指定されている場合、チェックする
       if (lastName != null && !person.LastName.Contains(lastName))
       {
           return false;
       }

       //名が指定されている場合、チェックする
       if (firstName != null && !person.FirstName.Contains(firstName))
       {
           return false;
       }

       return true;
   }

where句には、ブール値を返すものであればメソッドも記述できますので、
条件の判定処理をメソッドの中に記述することにしました。

次回はorderby句です。

さて、本日の記事から複数回に分けてクエリの文法を紹介していきます。
本日はfrom句とselect句です。サンプルのデータソースには1回目の記事で使用したPersonクラスの配列を使用します。

    class Program
    {
        private Person[] persons = null;

        Program()
        {
            //クエリのデータソースを初期化する
            persons = new Person[]
            {
                new Person(){ LastName="Tanaka",FirstName = "ichiro", Age = 20},
                new Person(){ LastName = "Sato",FirstName = "Kazuhiro",Age = 25},
                new Person(){ LastName = "Wada",FirstName = "Takashi",Age = 30},
                new Person(){ LastName = "Asada",FirstName = "Kenji",Age = 40},
                new Person(){ LastName = "Tanaka",FirstName = "Tatsuta",Age = 26},
                new Person(){ LastName = "Yoshino",FirstName = "Taro",Age = 27}
            };
        }
    }


from句
from句はデータを抽出する元となるデータソースを指定するための構文で、「from A in B」の形をとります。
またクエリは必ずfrom句から始まっている必要があります。

Aは「範囲変数」と呼ばれるもので、クエリ式の中で、これを利用して条件を指定したり、並び順を指定したりします。

Bにはデータソースを指定します。データソースは、IEnumerable、または、IEnumerable(T)またはその派生型である必要があります。
身近なところで言うと配列やArrayList、List(T)クラスなどがあります。

select句
select句はクエリの実行結果を列挙する際の値の型を指定するためのものです。

クエリを格納する変数の型はIEnumerable(T)になりますが、クエリの結果を取得する際には、
この型のインスタンスからforeach構文を使って値を列挙します。select句で指定するものは、
foreach文が返す値、すなわちforeach(A in B)のAの型を指定するためのものです。

さて、from句とselect句を説明したところで、以下のサンプルを見て下さい。
from句とselect句からなるシンプルなクエリを実行しています。

   void SelectAll()
   {
       IEnumerable<Person> = from person in persons
                   select person;

       foreach (Person person in query)
       {
           Console.WriteLine("{0}-{1}-{2}", person.LastName, person.FirstName, person.Age);
       }
   }

from句でpersonsという配列をデータソースとして指定し、範囲変数としてpersonを記述しています。
select句には範囲変数「person」をそのまま記述しています。クエリ結果を列挙するforeach文の繰り返し変数が
Person型になっていて、範囲変数「person」の型と一致していることが分かると思います。

また、以下のようにすることで、列挙される値を名前だけにすることも出来ます。

   void SelectFirstNameAll()
    {
        IEnumerable<String> query = from person in persons
                    select person.FirstName;

        foreach (String name in query)
        {
            Console.WriteLine("{0}", name);
        }
    }

selectにはメソッドを指定することも出来ます。

   void SelectFirstNameAll()
   {
       IEnumerable<String> query = from person in persons
                                   select String.Format("{0}さん", person.FirstName);

       foreach (String name in query)
       {
           Console.WriteLine("{0}", name);
       }
   }

varについて
上記で紹介してSelectAll()は以下にように記述することが可能です。

   void SelectAll()
   {
       var query = from person in persons
                   select person;

       foreach (var person in query)
       {
           Console.WriteLine("{0}-{1}-{2}", person.LastName, person.FirstName, person.Age);
       }
   }

[var]はC#3.0から導入されたキーワードで、ローカル変数を宣言する際に使用できるものです。
varで宣言された変数は同時に初期化される必要があります。そして、初期化時に設定された値の型によって
暗黙的に型指定がなされます。

上記の例で言うと変数宣言の述語(クエリの部分)の型がIEnumerable<Person>であるため、
変数queryはIEnumerable<Person>として暗黙的に型指定されます。

これはJavaScriptの変数のようにどの型も受け付けるといったようなものであは有りません。
上記のサンプルに以下のような一行を加えると、コンパイルエラーになってしまいます。

   void SelectAll()
   {
       var query = from person in persons
                   select person;

       foreach (var person in query)
       {
           Console.WriteLine("{0}-{1}-{2}", person.LastName, person.FirstName, person.Age);
       }

        query = "test";
   }

変数クエリは宣言時に暗黙的にIEnumerable<Person>として型指定されているため、その変数に対して他の型を持つ
値を代入することは出来ません。

次回はwhere句を説明します。

本日はLINQを使う上でおさえておきたい基本的な事柄についてお話をします。

LINQプロバイダ

前回の記事ではPersonという自作クラスの配列に対してシンプルなLINQクエリを実行するサンプルを
紹介しましたが、LINQでは通常のオブジェクトの他にデータベースやXML、Webサービスなど様々なデータソースに対して
クエリを実行することが出来ます。

標準ライブラリで提供されているものだけでも以下のものがあります。

・LINQ to Object
・LINQ to DataSet
・LINQ to SQL
・LINQ to Entities
・LINQ to XML

「LINQ to Object」は前回の記事で紹介したユーザ定義クラスのコレクションに対してクエリを実行するタイプのものです。
「LINQ to DataSet」、「LINQ to SQL」、「LINQ to Entities」はいずれもデータベースに対してクエリを実行するものです。
「LINQ to XML」はその名の通りXMLに対してクエリを実行するためのものです。

「LINQ to Object」はIEnumerable、IEnumerable<T>を直接使用して、ユーザ定義クラスのコレクションに対してクエリの
実行を行います。その他のものについてはLINQプロバイダと呼ばれる中間クラスを通して、目的のデータソースに対して、
クエリが実行されます。

LINQプロバイダはAmazon、Google、Twitter、Excelなど様々なデータソースに対するものが提供されています。
「Charlie Calvert's Community Blog」に詳細が紹介されています。

また、System.Linq名前空間の「IQueryable」または、「IQueryable<T>」と「IQueryProvider」を実装するクラスを作成し、
独自のLINQプロバイダを作成することも可能です。

LINQとC#3.0の言語仕様

LINQはC#3.0と.NET Framework3.5から導入された新機能ですが、その正体は.NET Framework3.5から追加されたAPIの1つなのです。
そして、C#3.0にはそれらLINQに関するAPIを使いやすくするための「シンタックスシュガー」が追加されているのです。

※シンタックスシュガー(糖衣構文)とはプログラムコードを読み書きしやすくするための構文のことを言います。

実は、前回の記事で扱った以下のクエリも新しく導入されたシンタックスシュガーの1つなのです。

  IEnumerable query = from person in persons
                        orderby person.Age
                        select person;

これをシンタックスシュガーを使わない形で記述すると以下にようになります。

   IEnumerable query = persons.OrderBy((x) => x.Age).Select((x) => x);

内容はまた後日説明いたしますが、後者の記述の仕方より、前者の方が明らかに見易いですよね。

このようにLINQを扱うためにはC#3.0から導入された新たな言語仕様を学ぶ必要が出てきます。

余談ですが、C#の普及が進まない理由の1つに技術革新の速度が早すぎるというものがあると記事で読んだことがあります。
これは技術者としては嬉しいことである反面、キャッチアップしていくことが大変であるという側面もあります。

そうこうしているうちにVisual Studioの次期バージョンと共にC#4.0もリリースされることになっています。
みなさまはC#の技術革新の速度についてどのようにお感じになられていますでしょうか?

さて、次回からLINQ to Objectを使ってLINQクエリの仕様を掘り下げていきたいと思います。

今回の記事からC#3.0とNET Framework3.5から導入されたLINQの入門記事を連載していきたいと思います。

LINQとは「統合言語クエリ」と略されるもので、C#のコードの中にSQLに似た構文のクエリを記述し、それにより
配列など様々なデータの集合から検索や集計を行うことが出来るというものです。

今回はまずサンプルにより直感的にLINQに実感してもらえればと思います。

まず、人の氏名と年齢を管理するためのPersonクラスを定義します。

  class Person
  {
      public String LastName {  get;  set; }
      public String FirstName { get; set; }
      public int Age { get; set; }
  }

そして、Personクラスの配列を作成します。

  Person[] persons = new Person[]
  {
     new Person(){ LastName="Tanaka",FirstName = "ichiro", Age = 20},
     new Person(){ LastName = "Sato",FirstName = "Kazuhiro",Age = 25},
     new Person(){ LastName = "Wada",FirstName = "Takashi",Age = 30},
     new Person(){ LastName = "Asada",FirstName = "Kenji",Age = 40},
     new Person(){ LastName = "Tanaka",FirstName = "Tatsuta",Age = 26},
     new Person(){ LastName = "Yoshino",FirstName = "Taro",Age = 27}
  };

※同じくC#3.0から登場したオブジェクト初期化を使用してPersonクラスのインスタンス化を行っています。

次にこのPersonクラスの配列からLINQクエリを使用して、年齢の若い順にインスタンスを抽出し、氏名と年齢をコンソールに表示します。

     //LINQクエリ
     IEnumerable<Person> query = from person in persons
                  orderby person.Age
                  select person;

     //クエリを実行し、結果を列挙する
     foreach (Person person in query)
     {
          Console.WriteLine("{0} {1} Age:{2}", person.LastName, person.FirstName, person.Age);
     }

結果は以下のように表示されます。

Tanaka ichiro Age:20
Sato Kazuhiro Age:25
Tanaka Tatsuta Age:26
Yoshino Taro Age:27
Wada Takashi Age:30
Asada Kenji Age:40

LINQ以前のコードだと自作クラスをソートするのは少し面倒くさい作業でしたが、LINQを使うと
こんなに簡単にソート処理を行うことができます。

サンプルを使ってLINQを少しだけ解説します。(詳細は次回以降で。)
LINQクエリとは指定されたデータの集合(今回のサンプルでは配列)を列挙する方法を指定するものなのです。従って
クエリを受ける変数の型をIEnumerable<Person>と宣言しています。

※IEnumerableはforeach可能なクラスが実装するインターフェースです。

LINQクエリはSQLに慣れた方であれば何となく推測できると思いますが、少し詳しくを見ていきます。

まず、from句はデータの取得元を指定する部分で、[from A in B]という形をとります。Bにはforeachできるタイプの
もの(IEnumerable、IEnumerable<T>を実装するクラス)なら何でも指定可能です。Bをデータソースと言います。
AはBから順番に取り出されたものを格納する範囲変数と呼ばれるものです。

この範囲変数を使用して抽出条件を指定したり、並び順を指定したりします。
上記のサンプルでも、order by person.Ageのように使用していますよね。

orderby句は並び順を指定するためのものです。
select句はクエリの結果の型を指定するためのものです。上記サンプルをselect person.Ageとすると、年齢だけを
クエリの結果として返すようになります。

最初にも説明しましたようにLINQクエリはデータソースから各要素を列挙する方法を指定するためのもので、
列挙されたデータそのものではありません。

データの列挙自体はforeachを実行することにより始めて実行されます。

今回のサンプルでは、紹介しませんでしたがLINQクエリではwhere句、group句、join句などを使用し
高度なクエリを記述することが出来ます。

LINQクエリは通常のオブジェクトの他にデータベースやXMLに対してもクエリを実行することが可能ですが、
私としては最大の魅力はやっぱり通常のオブジェクトに対してクエリを実行できることだと思います。

個人的に作るプログラムではデータベースを使用しないケースも多いので、LINQの機能はとても重宝すると思います。

次回はLINQの基礎を説明します。

上記広告は1ヶ月以上更新のないブログに表示されています。新しい記事を書くことで広告を消せます。