気楽なソフト工房

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



mykonos2008

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

以前の記事で、SilverlightでActualHeightやActualHeightを取得する方法を紹介しました。
Silverlightで発生するActualHeightやActualWidthを取得できない現象は同じくWPFでも発生します。

対処方法や考え方はSilverlightと同じなのですが、コードレベルでは少し異なる部分もあるので、
あらためて記事にしたいと思います。

まず、この現象が発生する原因については、Silverlight版の記事を参照してください。

以下は現在私が開発しているソフトのMainWindowのコンストラクタです。
InitializeComponent()でコントロールが初期化された後に各コンポーネントのサイズを
調整する処理を記述しています。

        public MainWindow()
        {
            InitializeComponent();

            //遅延処理を行う
            Dispatcher.BeginInvoke(new Action(() =>
                {
                    //DockPanelのサイズを設定する
                    _rootPanel.Width = _rootCanvas.ActualWidth;
                    _rootPanel.Height = _rootCanvas.ActualHeight;

                    //ブラウザコントロールのサイズを設定する
                    _browser.Height = _rootCanvas.ActualHeight / 2;
                })
            ,DispatcherPriority.Loaded);
        }

Silverlight版と異なる点が2つあります。

1つ目は、BeginInvokeの第一引数の記述方法です。汎用デリゲートActionのコンストラクタにラムダ式の匿名メソッドを
指定して初期化しています。Silverlight版では、第一引数に直接匿名メソッドを記述するだけでもOKでした。

2つ目は、DispatcherPriorityを指定している点です。Silverlight版のBeginInvoke()では、DispatcherPriorityは指定
することが出来ませんでした。WPFでは逆にこれを正しく指定しないとActualHeightやActualWidthを取得することが出来ません。

レイアウトシステムの処理は指定できる優先順位の中で3つ目に高いRenderが割り当てられています。
そのため、Actualサイズを取得する処理にはこれより低い優先順位を指定しないといけません。

以上です。
本日は、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句を説明します。

今回もWPFのTipsです。最近WPFでソフトを作っているので、紹介したいものがたくさんあるのです。

WPFで開発をしているとWindowsフォームにあるSplitterやSplitContainerみたいに画面を分割するための
コントロールが無いことに気づくと思います。

でも、ご心配なく。ちゃんとあります。GridパネルとGridSplitterというコントロールを使用します。
以下は最近私が開発しているソフトの画面なのですが、画面をGridSplitterを使って左右に分割しています。



画面からは読み取れませんが、画面の左側にTreeViewと右側にListViewを配置しています。
以下は該当部分のXAMLになります。

    <Grid>
        <Grid.ColumnDefinitions>
            <ColumnDefinition/>
            <ColumnDefinition/>
        </Grid.ColumnDefinitions>
        
        <TreeView Grid.Column="0"></TreeView>
        <GridSplitter VerticalAlignment="Stretch" Width="2"/>
        <ListView Grid.Column="1"></ListView>
    </Grid>

まず、Gridパネルに列を2列定義します。そして、TreeViewを左側の列にListViewを右側の列に配置します。
そして、その真ん中にGridSplitterというWindowsフォームのSplitterに該当するコントロールを配置します。

今回は画面を左右に分割しているので、GridSplitterのVerticalAlignmentにStreah(縦に伸ばす)を指定しています。

GridSplitterを配置する列は定義する必要はありません。

以上で、完了です。Splitter部分にカーソルを持っていくと、左右のコントロールのサイズを調整することが出来ます。

今回の記事は久しぶりにWPFのTipsを紹介します。
Windowsアプリケーションを開発する際、メインウィンドウが起動する前に
何らかの処理を行いたい場合が有ると思います。

例えば、メインウィンドウの起動前にログイン用のダイアログを表示し、認証に失敗した場合は、
そのままアプリケーションを終了させる場合もあると思います。

Windowsフォームの場合、このような時には、FormクラスのLoadイベントのハンドラに処理を記述していました。

WPFでもWindowクラスに似たようなLoadedイベントというものがあり、ここに書けばいいのかと思い記述してみたところ、
既にメインウィンドウが表示されてから、ハンドラに書いた処理が実行されてしまいました。

ドキュメントを参照すると「要素の配置、描画、および操作の準備が完了したときに発生します」と書かれているので、
このタイミングでは遅いみたいです。。。

何か代わるものが無いかと探してみたところ、ApplicationクラスのStartupイベントで同じことが出来ました。

[App.xaml]
<Application x:Class="Test.App"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    StartupUri="MainWindow.xaml" Startup="Application_Startup">

まず、[App.xaml]でStartupイベントのハンドラを登録します。
次に、[App.xaml.cs]にイベントハンドラのコードを追加します。

※[App.xaml]のデザインビューでプロパティウィンドウのイベント「Startup」をダブルクリックするだけで
ハンドラの登録とハンドラメソッドの追加は自動的に行うことが出来ます。

[App.xaml.cs]
    private void Application_Startup(object sender, StartupEventArgs e)
    {
       //メインウィンドウ起動前に行う処理を記述する
    }

処理を行った結果、アプリケーションのそのまま終了させたい場合は、Applicationクラスの「Current.Shutdown()」
メソッドを使用します。

「App」クラスはApplicationクラスの子クラスなので、以下のように記述するだけでOKです。

    private void Application_Startup(object sender, StartupEventArgs e)
    {
       try
       {

           //メインウィンドウ起動前に行う処理を記述する
       }
       catch
       {
           Current.Shutdown();
       }
    }

少し空いてしまいましたが、本日はGAE開発日記の11回目です。台帳一覧からクリックされた台帳の詳細を表示する部分のコードを紹介します。
以下が台帳一覧画面と台帳詳細画面のイメージです。





まず台帳一覧側のJSPのコードを見てください。

[ledgerlist.jsp]
・・・・(省略)

<table width="500px">
<tr>
    <td style="background-color: #ccccff;">
       &nbsp;
    </td>
    <td width="65%" style="background-color: #ccccff;">
       台帳名
    </td>
    <td style="background-color: #ccccff;">
       作成者</td>
    </tr>

<%
   List<Ledger> ledgerList = (List<Ledger>)request.getAttribute("ledger_list");

   for(Ledger ledger : ledgerList)
   {
%>
   <tr>
       <td>
          <input name="chk_id" value="<%=  ledger.getKey().getId() %>" type="checkbox"/>
       </td>
       <td>
          <a href="#" onClick="showDetail('<%= ledger.getKey().getId() %>')">
              <%= ledger.getLedgerName() %>
          </a>
       </td>
       <td>
              <%= ledger.getCreatedBy() %>
       </td>
   </tr>
<%
   }
%>
</table>

・・・・(省略)

<input type="hidden" name="ledger_id"/>


台帳名のリンクがクリックされたら、showDetail()というJavaScriptの関数が呼ばれるようにしています。
Scriptの内容はこんな感じです。

[ledgerlist.jsp]
function showDetail(ledgerId)
{
    document.fm.action = "/ctl/LedgerDetail";
    document.fm.ledger_id.value = ledgerId;
    document.fm.submit();
}

formの要素としてname属性にledger_idという値を持つhiddenタグをあらかじめ作成しておき、
スクリプトでそのタグの値にクリックされた台帳レコードのIDを設定してフォームをサブミットしています。

次にリクエストを処理するハンドラクラスのコードを見てください。

[LedgerDetail.java]

    public void execute(HttpServletRequest req, HttpServletResponse resp)
            throws Exception {
        
        //詳細を表示する台帳のIDを取得する
        String id = req.getParameter("ledger_id");
        
        //台帳を取得する
        LedgerDao dao = new LedgerDao();
        Ledger ledger = dao.getLedger(id);
        
        //リクエストに台帳を設定する
        req.setAttribute("ledger", ledger);
        
        //Forwards to JSP.
        RequestDispatcher dispatcher =  req.getRequestDispatcher("/pages/ledgerdetail.jsp");
        dispatcher.forward(req, resp);

    }

リクエストから台帳のコードを取得し、Daoクラスに渡して台帳クラスのインスタンスを取得しています。
そして、取得した台帳クラスのインスタンスをRequestオブジェクトに設定し、JSPにリクエストのフォワードしています。

次はDaoクラスのコードです。

[LedgerDao.java]
    public Ledger getLedger(String id)
    {
        //Initialize Persistent Manager.
        PersistenceManager pm = PMFHelper.get().getPersistenceManager();
        
        Ledger ledger = null;
        
        try
        {
            //String型のIDをlong型に変換し、Ledgerクラスのインスタンスを
            //データストアから取得する
            long longId = Long.parseLong(id);
            ledger = pm.getObjectById(Ledger.class,longId);

            //データストアからデータをロードするための処理
            ledger.getCreatedBy(); //台帳の作成ユーザ
            
            //台帳を使用可能なユーザ
            for(LedgerUser user : ledger.getLedgerUser())
            {
                user.getUser();
            }
        }
        finally
        {
            pm.close();
        }
    
        return ledger;
    }

IDからインスタンスを取得するためのメソッドはPersistenceManagerクラスのgetObjectById()メソッドです。

黄色時の部分はデータストアから各属性のデータをロードするための処理です。GAEのデータストアでは、
オブジェクトの属性の型が他のクラスの場合、接続が開かれているうちにその属性のアクセスしておかないと
ロードが行われません。

今回の例で言うと台帳クラスの台帳名(ledgerName)は自動的にロードされますが、作成ユーザ(createdBy)や
台帳ユーザ一覧(List<LedgerUser>)はアクセスしないとロードされません。

台帳の詳細画面では台帳を使用可能なユーザ等も表示するためにこの処理が必須になります。

最後に詳細画面のJSPです。

[ledgerdetail.jsp]
・・・・(省略)
<%
  Ledger ledger = (Ledger)request.getAttribute("ledger");
%>
・・・・(省略)
<div style="text-align: left;font-weight: bold;">台帳名</div>
<div style="text-align: left;"><input type="text" value="<%= ledger.getLedgerName() %>" size="50"></div>
・・・・(省略)
<table width="400px">
<tr id="acthdr" style="background-color: #ccccff;"><th width="5%"></th><td>アカウント</td></tr>

<!-- 使用可能ユーザ一覧 -->
<% 
    for(LedgerUser user : ledger.getLedgerUser())
    {
%>
<tr>
   <td>
<%
      //作成ユーザ以外の使用可能ユーザを削除できるようにチェックボックスの表示を制御する
      if(!user.getUser().getEmail().equals(ledger.getCreatedBy().getEmail()))
      {
%>
       <input type="checkbox"/>
<%
      }
      else
      {
%>
       &nbsp;
<%
      }
%>
   </td>
   <td>
       <%= user.getUser().getEmail() %>
   </td>
 </tr>
<%
    }
%>
</table>

少しづつですがまた進めていきます。

C#でWindowsのアプリケーションを開発する際、ユーザが行ったアプリケーションの設定をどのように保存するのか
ということについて頭を悩ませることがあると思います。

本日はアクセス解析ソフト「Crete」を開発した際に使用したアプリケーション設定を保存する方法を紹介します。

「Crete」で使用したアプリケーション設定保存のテクニックは、アプリケーションの設定情報を管理するクラスのオブジェクトを
XMLにシリアライズし、ファイルに保存するといったものです。

それでは少しずつコードを見ていきます。アプリケーション設定を保存するクラスはSingletonパターンで作成します。
こうすることでアプリケーションの様々な箇所から同一の設定クラスのインスタンスに簡単にアクセスすることが
出来るようになります。

[AppSettings.cs]
  public class AppSettings
  {
      private static AppSettings _appSettings = null;
      private List<String> _urls = null;

      //クラス内からしかインスタンス化できないようにする
      private AppSettings(){}

      public static AppSettings GetInstance()
      {
          if (_appSettings == null)
          {
              _appSettings = new AppSettings();
              _appSettings.Urls = new List();
          }

          return _appSettings;
      }

      //解析対象のページのURLのリストを管理するプロパティ
      public List<String> Urls
      {
          get { return _urls; }
          set { _urls = value; }
      }
  }

これだけでは、設定を保存したり、読み込んだりすることが出来ないので、それを行うためのメソッドをAppSettingsクラスに
追加します。

[AppSettings.cs]
  public class AppSettings
  {
      //設定をロードする
      public static void Load()
      {
          //ユーザ毎のアプリケーションデータディレクトリに保存する
          String appPath = String.Format("{0}\\{1}", Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData),
                                                        "\\Crete\\settings.xml");

          if(File.Exists(appPath))
          {
              XmlSerializer serializer = new XmlSerializer(typeof(AppSettings));

              using (FileStream stream = new FileStream(appPath, FileMode.Open))
              {
                  _appSettings = serializer.Deserialize(stream) as AppSettings;
              }
          }
      }

      //設定を保存する
      public static void Save()
      {
          String appPath = String.Format("{0}\\{1}", Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData),
                                                        "\\Crete\\settings.xml");

          XmlSerializer serializer = new XmlSerializer(typeof(AppSettings));

          using (FileStream stream = new FileStream(appPath,FileMode.Create))
          {
              serializer.Serialize(stream, _appSettings);
          }
      }
  }

クラスのインスタンスの保存、復元は「XmlSerializer」クラスを利用することで簡単に行うことが出来ます。
「XmlSerializer」はpublicなプロパティとフィールドが保存、復元の対象となります。詳しく解説しませんが、
独自に定義したクラスを型とするフィールドを保存することも可能です。

この設定クラスをアプリケーションから利用する際は以下のようにします。

まず、フォームのLoadイベントのハンドラで設定をロードします。

    public partial class MainForm : Form
    {
        protected override void OnLoad(EventArgs e)
        {
            ・・・・・・・・・・・・・

            //設定をロードする
            AppSettings.Load();
        }
    }

そして、設定を利用する箇所で、以下のようにして設定クラスのプロパティにアクセスします。


  String<String> urls = AppSettings.GetInstance().Urls;

  foreach (String url in urls)
  {
      _address.Items.Add(url);
  }

設定の保存は、フォームのClosingイベントで行っています。

    public partial class MainForm : Form
    {
        protected override void OnFormClosing(FormClosingEventArgs e)
        {
            try
            {
                //設定を保存する
                AppSettings.Save();
            }
            catch { }
        }
    }

この方法はWindowsアプリケーションを開発する際には私が頻繁に使用する方法なのですが、最大の利点は、
様々な種類のアプリケーション設定項目を設定クラスにそれに対応するプロパティを追加するだけで、
保存することが出来るという点です。

さて、本日は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句を説明します。

今月から当ブログのアクセスログを元に、月毎にブラウザとOSのシェアを掲載していこうと思います。

当ブログはC#の話題が中心なのでOSのシェアに関しては、必然的にWindows系OSが多い結果になるとは
思いますが、Windows系OSの中でのシェアという意味では参考になるかと思います。

ブラウザのシェア

----------------------------
1位 Firefox3.0 42.4%
2位 IE7.0 29.3%
3位 IE6.0 20.2%
4位 IE8.0 3.2%
5位 Google Chrome 2.4%
・・・
その他
----------------------------

Firefox3.0がIE7.0をおさえて1位となっています。IE系のブラウザは全て合わせても52.7%となっており、
全体の1/2程度になっています。

OSのシェア

Windows系以外のOSのシェアは1.9%なので、以下、Windows系OSについてのみ掲載します。

----------------------------
1位 XP 73.2%
2位 Vista 23.4%
3位 2000 1.4%
----------------------------

Vistaが普及していない現実はここでも示される結果となりました。