All posts by Ishisaka

C# eventのオーバーライドと基底クラスで発生するイベントの処理

C#のeventはvirtualが宣言できて、派生クラスでoverrideできますが、そのオーバーライドされたイベントに登録したイベントハンドラを基底クラス側から呼び出せません。これはeventは宣言された同一クラス内でのみ発生させることができると言うC#の言語仕様なので当たり前と言えば当たり前の動作なのですが、適切な処理をしていないがために基底クラス側でイベントを発生させたときにExceptionが発生してしまったりと言うことがあります。(別の言い方を知れば派生クラスから基底クラスのイベントを発生させることができません。)

まぁ頭の悪い子(俺)はそれでずっとうじうじ悩んだりするわけです。

具体的にはイベントをオーバライドしたら、そのオーバーライドしたイベントの基底クラスのイベントのイベントハンドラを用意し、そのイベントハンドラで、オーバーライドしたイベントを呼び出します。ようはイベントが伝達できるようにすればいいわけですねって、オーバーライドって文字がたくさん出てきて何かよくわかりませんね。と言うことでサンプルを以下にのせます。

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

namespace EventTest
{
    class Program
    {
        static void Main(string[] args) {
            Test test = new Test();
            test.TestEvent += new EventHandler(test_TestEvent);
            test.Fire();
            test.BaseFire();
            Console.Read();
        }

        static void test_TestEvent(object sender, TestEventArgs e) {
            Console.WriteLine(e.Message);
        }
    }

    //基底クラス
    public class TestBase
    {
        //イベントをvirtualで宣言する。
        internal virtual event EventHandler TestEvent;
        //イベントを発生させる
        public void BaseFire() {
            TestEventArgs args = new TestEventArgs("Fired on Base class");
            TestEvent(this, args);//派生クラス側で処理がないとイベントを発生できないので、ここでExceptionが発生する。
        }

    }

    //派生クラス
    public class Test : TestBase
    {
        internal override event EventHandlerTestEvent;

        public Test() {
            //基底クラスのイベントにイベント伝達用のイベントハンドラを登録する。
            base.TestEvent += new EventHandler(Test_TestEvent);
        }
        //イベント伝達用のイベントハンドラ
        void Test_TestEvent(object sender, TestEventArgs e) {
            //利用先に基底クラスのイベントを伝達する
            this.TestEvent(sender, e);
        }
        //イベントを発生させる
        public void Fire() {
            TestEvent(this, new TestEventArgs("Fired on Extended class"));
        }
    }

    public class TestEventArgs : EventArgs
    {
        public TestEventArgs(string message) {
            this.Message = message;
        }
        public string Message { get; set; }
    }
}

実行結果

Fired on Extended class
Fired on Base class

当たり前と言えば当たり前のコードなんですがeventのvirtual / override含めて書籍やネットにあまりサンプルが無いようなので載せてみました。

Linq to XML 入門 その4 結合(JOIN)その2

忘れた頃にやってくる連載記事です。

今回は、やらないと前回書いた左外部結合とグループ化結合について説明します。

また、サンプルで使用するXMLファイルで、前回との違いは、category.xmlにカテゴリを一つ追加しています。



  
    飲料
  
  
    前菜
  
  
    主菜
  
  
    デザート
  
  
    お持ち帰り
    

グループ化結合

グループ化結合は、階層データ構造を作成する場合に便利です。グループ化結合は、最初のコレクションの各要素と、2 番目のコレクションの相関関係を持つ要素のセットを組み合わせたものです。

使用するXMLは内部結合のものと同じです。

以下の例ではカテゴリごとの料理のリストを作成しています。

XDocument menu = XDocument.Load(@"..\..\menu.xml");
XDocument category = XDocument.Load(@"..\..\category.xml");
 
var menuList = from c in category.Descendants("Category")
               join m in menu.Descendants("item")
               on
                   (string)c.Attribute("id")
               equals
                   (string)m.Attribute("CategoryId")
               into menusByCategory
               select new {
                   ID = c.Attribute("id"),
                   CategoryName = c.Element("CategoryName").Value,
                   Menus = from me in menusByCategory select new { Name = me.Element("name").Value }
               };
 
foreach (var c in menuList) {
    Console.WriteLine("{0} - {1}", c.ID, c.CategoryName);
    foreach (var m in c.Menus) {
        Console.WriteLine("\t{0}", m.Name);
    }
}

1,2行目でXML文書を読み込みます。

5行目から今回新しく説明するポイントです。

5行目のjoin句で、4行目のfrom句でcategory.xmlから取り出したxmlのエレメントに結合するmenu.xmlのエレメントを指定します。

6行目からon句から始まり9行目までが結合条件で、この場合にはcategory.xmlがもつidアトリビュートとmenu.xml側が持つ CategoryIdアトリビュートの値の一致をもって結合するようにしています。アトリビュート同士の値が同値であることの確認には"=" (オペレータ)ではなくequals句を使う必要があります。正確には

from <左辺XML>

join <右辺XML>

on <左辺条件> equals <右辺条件>

となります。

10行目ではinto句を使ってcategory.xmlのエレメントが持つidアトリビュートごとにmenu.xmlのエレメントをグループ化して、category.xmlのエレメントに結合します。

14行目ではselectをネストして、グループ化された中からメニューの名前のリストを作成しています。

動作結果

d="1" - 飲料
       コーラ
       アイスコーヒー
       ブレンドコーヒー
       生ビール
d="2" - 前菜
       フレンチフライ
       湯で海老
       チーズ盛り合わせ
       オニオングラタンスープ
       シェフのお任せミニサラダ
       シェフのお任せスープ
d="3" - 主菜
       御殿場高原で育った子羊のロースト
       あしたか牛のフィレ肉を使ったビーフシチュー
       沼津港スズキのグリル
       焼津マグロのカルパッチョ
       あしたか牛のサーロインステーキ
d="4" - デザート
       韮山苺を使ったショートケーキ
       西浦みかんのババロワ
       朝霧高原の牛乳から作ったティラミス
d="5" - お持ち帰り

左外部結合

左外部結合は、2 番目のコレクションに相関関係を持つ要素があるかどうかに関係なく、最初のコレクションの各要素が返される結合です。LINQ を使用すると、グループ結合の結果に対して DefaultIfEmpty を呼び出すことで、左外部結合を実行できます。

以下の例は、カテゴリーID、カテゴリー名、メニュー名を表示するプログラムで、カテゴリーに対応するメニューが無くてもカテゴリーIDとカテゴリー名は表示します。

処理の手順は次のようになります。

最初手順ではグループ結合を使用して内部結合を実行します。今回の例ですと、先ほどと同じようにmenu.xmlのエレメントのリストは、 category.xmlのエレメントが持つidアトリビュート と一致する menu.xmlのエレメントに基づいて、category.xmlのエレメントの一覧に内部結合されます。

2 番目の手順では、右側のコレクションに一致する要素がない場合でも、最初 (左側) のコレクションの各要素を結果セットに含めます。これを行うには、グループ結合から一致する要素の各シーケンスで DefaultIfEmpty を呼び出します。以下の例では一致する menu.xmlのエレメントの各シーケンスで DefaultIfEmpty が呼び出されます。一致する menu.xmlのエレメントのシーケンスがすべての category.xmlのエレメントに対して空の場合、各 category.xmlのエレメントが結果コレクションに表示されるように、1 つの既定値を含むコレクションが返されます。

var menuList = from c in category.Descendants("Category")
               join m in menu.Descendants("item")
               on
                (string)c.Attribute("id")
               equals
                (string)m.Attribute("CategoryId")
               into menusByCategory
               from mx in menusByCategory.DefaultIfEmpty(
                new XElement("item", new XAttribute("id", c.Attribute("id").Value), new XElement("name", ""), new XElement("price", "")))
               select new {
                   ID = c.Attribute("id"),
                   CategoryName = c.Element("CategoryName").Value,
                   MenuName = mx.Element("name").Value
               };
 
foreach (var c in menuList) {
    Console.WriteLine("{0} - {1} \t:\t{2}", c.ID, c.CategoryName, c.MenuName);
}

11行目はメニューで使われていないカテゴリが表示できるように、DefaultIfEmptyメソッドでmenusByCategory内の各シーケンスを調べ、シーケンスが空でないならシーケンスの各要素をmxに入れ、シーケンスが空であるなら、規定値としてDefaultIfEmptyメソッドの引数で与えている値がすべて空のitemエレメントをmxに入れています。(ここでは値の無いエレメントを追加していますが、それが適切かどうかは場合によります。)

動作結果

id="1" - 飲料   :       コーラ
id="1" - 飲料   :       アイスコーヒー
id="1" - 飲料   :       ブレンドコーヒー
id="1" - 飲料   :       生ビール
id="2" - 前菜   :       フレンチフライ
id="2" - 前菜   :       湯で海老
id="2" - 前菜   :       チーズ盛り合わせ
id="2" - 前菜   :       オニオングラタンスープ
id="2" - 前菜   :       シェフのお任せミニサラダ
id="2" - 前菜   :       シェフのお任せスープ
id="3" - 主菜   :       御殿場高原で育った子羊のロースト
id="3" - 主菜   :       あしたか牛のフィレ肉を使ったビーフシチュー
id="3" - 主菜   :       沼津港スズキのグリル
id="3" - 主菜   :       焼津マグロのカルパッチョ
id="3" - 主菜   :       あしたか牛のサーロインステーキ
id="4" - デザート       :       韮山苺を使ったショートケーキ
id="4" - デザート       :       西浦みかんのババロワ
id="4" - デザート       :       朝霧高原の牛乳から作ったティラミス
id="5" - お持ち帰り     :

このように「お持ち帰り」はメニューに存在しませんが表示されています。

ということでひとまず結合を含むクエリ式の説明はこれで終わりです。

それではまた次回!(あるのかー)

これまでの記事:

参考図書:

Essential LINQ (Microsoft .NET Development Series)

Essential LINQ (Microsoft .NET Development Series)

Charlie Calvert

Addison-Wesley Professional 2009-03-22

売り上げランキング : 109833

Amazonで詳しく見る by G-Tools

プログラミングMicrosoft LINQ (マイクロソフト公式解説書 Microsoft Visual Studi)

プログラミングMicrosoft LINQ (マイクロソフト公式解説書 Microsoft Visual Studi)

小高 太郎 (株)オーパス・ワン

日経BPソフトプレス 2009-05-25

売り上げランキング : 43591

Amazonで詳しく見る by G-Tools

Linq to XML 入門 その4 結合(JOIN)

しばらく間が開いてしまいすいませんです。前回は基本的なクエリーまで説明したので、今回からはもう少し複雑なクエリー式の説明をします。

今回はJOINを説明します。

JOIN(結合)はLinqにおいてもリレーショナルデータベースの結合と同じく一定の条件の下データ同士を関連付けます。

結合(Join)の概念がちょっとわかりにくいという方はSQLの入門書で結合(Join)について勉強してみるといいと思います。このJOINあたりからが実際にはLinqの非常に強力な使用方法になっていきます。リレーショナルデータベースのデータでもないものを簡単な書式でJoinできるというのは実はすごいことなのです。

Continue reading Linq to XML 入門 その4 結合(JOIN)

XML to Linq 入門番外編 : XDocumet.Load()でProxyの例外が発生する場合

前回までのサンプルではXDocument.Load(”URL”)でRSSのフィードをロードしてしましたが、Proxy経由でのアクセスが必要な場合に、Loadメソッド実行時に例外が発生します。

Loadメソッドは引数で指定されたURIがhttpであった場合に内部的にWebアクセスしていると思われますが、そのときにProxyに対しては考慮していないようです。また、Loadメソッドの引数やXDocumentのプロパティではProxyの設定をする方法が無いので、このような場合にはLoadメソッドで直接URIを指定するのではなく、Proxyの設定ができるWebClientとXmlReaderを使ってWEBサーバからXMLファイルを取得し、ストリーム化し、最終的にXMLデータとして取得する必要があります。

以下がそのサンプルです。

using System.Xml;
using System.Net;
using System.IO;

WebClient web = new WebClient();
WebProxy myProxy = new WebProxy("proxy:8080", true);
myProxy.Credentials = CredentialCache.DefaultCredentials;
web.Proxy = myProxy;
Stream stream = web.OpenRead(@"http://www.isisaka.com/blog/index.xml");

XmlReader reader = XmlReader.Create(stream);


//XDocument rss = XDocument.Load(@"http://www.isisaka.com/blog/index.xml");
XDocument rss = XDocument.Load(reader);

5行目でWebClientクラスのインスタンスを作ります。

6行目でWebProxyクラスのインスタンスを作り、Proxyサーバアドレス等を設定し、7行目でそのProxyの認証のタイプを設定します。(この場合はNTLM認証です)

8行目でWebClientのインスタンスにProxyを設定します。

9行目ではWEBサーバよりXMLデータをStreamとして取得します。

11行目ではstreamになっているXMLデータをXmlReaderに変換します。

15行目でLinq to XMLでXMLデータを使用できるようにXmlReaderからXDocumentにXMLデータをロードします。

XDocumentクラスのLoadメソッドでのURI指定は便利ですが、ネットワーク環境によってはこのような処理が必要になるので注意してください。また今回特に例外処理を入れてないのですが、実際には部分部分で例外処理が必要になる場合があるのでその注意もお願いします。

.NETでのProxy設定については以下のKBが参考になります。

[PRB] HTTP プロキシ サーバー経由で Web サービスを使用する .NET クライアントでエラーが発生する

http://support.microsoft.com/kb/318140/

早いところ次回が書けるといいなぁ。ではまた次回!

Liunq to XML 入門 その3 (Linq クエリ構文)

今回は予定を変えてLinqのクエリ式の構文について説明します。前回クエリの方法について説明しましたが、クエリ式の構文についての説明はしていなかったので、前回説明した基本的なクエリ式の構文について説明します。

基本的なクエリ

Linqでの基本的なクエリ式は以下のようになります。

from [範囲変数] in [データソース]
select [範囲変数]

まず、クエリ式はfrom句から始める必要があります。つぎに最低でもselect句で範囲変数を指定し、最終的に式が返すソースシーケンスの内容を確定します。

from句

from [範囲変数] in [データソース]

from句ではクエリもしくはサブクエリの対象となるデータソースとソースシーケンス内の各要素を表すローカルの範囲変数を指定します。ソースシーケンスとはデータソースの中でLinq式内での各操作の対象となる連続したデータのことです。

LinqのデータソースにはIEnumerable<T>インターフェイスあるいはIEnumerableとその派生インターフェイスを実装したオブジェクトだけを指定することができます。通常の配列やArrayList、ADO.NETのデータセットはIEnumerableインターフェイスを持つのでLinqの対象とすることができます。

selsct句

select [式]

selsct句はクエリ式が実行されたときに生成される値の型を指定します。クエリ式の結果はselect句の前の式の評価およびselect句の式の評価によって生成されます。

select句の一番単純な例は式に範囲変数を指定し、ソースシーケンスをそのまま式の値にすることですが、以下の例のようにこの式でデータ変換をすることができます。

XDocument rss = XDocument.Load(@"http://www.isisaka.com/blog/index.xml");   
//select句でデータ変換する   
IEnumerable query = from x in rss.Element("rss").Elements("channel").Elements("item")
                              select DateTime.Parse(x.Element("pubDate").Value);   
foreach (var d in query) {   
    Console.WriteLine(d.ToLongDateString());   
}

この例では4行のselect句の式でXMLのバリュー(string型)のデータをDateTime型に変換しているので、クエリ式の結果をIEnumerble<DateTime>型の変数に格納することができます。

条件付きクエリ

先ほどのもっとも単純なクエリ式ではソースシーケンスのデータに対し、なんのフィルタリングもかけずに値を返していましたが、実際には何らかのフィルタリング条件をつけて必要なデータだけをソースシーケンスから取り出したいことが多いと思います。

クエリ式において、このフィルタリング条件を指定するのがwhere句です。

from [範囲変数] in [データソース]
whewe [条件式]
select [式]

この例では7秒目でToListメソッドを使ってLinq式の結果をメモリ上に確定しています。

これの詳しい話についてはMSDNライブラリの「クエリの結果をメモリに格納する (C# プログラミング ガイド)」を参照してください。

それでは、また次回。

Linq to XML 入門その2 (基本的ななクエリー)

それでは、Linqでの基本的なクエリーの方法について説明していきます。

今回はXMLデータとして、このBlogのRSS 2.0フィードを利用します。

では、まずRSSを表示してみましょう。

Document rss = XDocument.Load(@"http://www.isisaka.com/blog/index.xml");
Console.WriteLine(rss.Declaration);
//Console.WriteLine(rss.FirstNode);
Console.WriteLine(rss);

その表示結果の一部。


    
        
            OPC Diary
            http://www.isisaka.com/blog/
            私的ソフトウェア開発あれこれ。
            ja
            Copyright 2009
            Thu, 14 May 2009 10:47:36 +0900
            http://www.sixapart.com/movabletype/
            http://blogs.law.harvard.edu/tech/rss
            
                プログラム言語 Axum
                
                    Axum

.NET Framework上でEr langっぽい(ぽいねあくまでも)並列言語実行環境を提供するプログラミング言語。Erlangっぽいので、基本的にメッセージパッ シングで実行単位(Agentというらしい)間でデータ交換を行うようだ。

これがこのまま製品化するとは思わないけど、 まぁF#の例もあるので、案外化けるかもだ。素直に考えればC#に取り込まれていくんだろうけど。

]]>
http://www.isisaka.com/blog/archives/2009/05/_axum.html http://www.isisaka.com/blog/archives/2009/05/_axum.html .NET オブジェクト指向・システム開発 Thu, 14 May 2009 10:47:36 +0900

RSSフィードを受信して、表示できる事が確認できたでしょうか。

では、このデータからBlog記事の投稿日とタイトルの一覧を作りたいと思います。

RSS 2.0ではitemエレメントに各Blog記事のデータが入っています。

XDocument rss = XDocument.Load(@"http://www.isisaka.com/blog/index.xml");
//XMLの木構造をたどって目的のElementに到達するやり方
var query = from x in rss.Element("rss").Elements("channel").Elements("item")
            select x;
foreach (var i in query) {
        DateTime pubDate = DateTime.Parse(i.Element("pubDate").Value);
        Console.WriteLine(pubDate.ToString() + "\t: " + i.Element("title").Value);

1行目ではRSSフィードを取り出しています。

3行目でLinqを使いRSSフィードからitemエレメントを取り出します。ここではRoorのrssエレメントから純にRSSの木構造たどってitemeエレメントまでを指定し検索もとを絞っています。itemエレメントは一つとは限らないのでElementsとして指定します。

5行目以降では検索結果をもとに更新日とタイトルをリスト表示します。

この様に特定のエレメントを検索対象とするには性格に木構造をたどる形で指定していく必要があり、その木構造が深いと指定するのが大変です。

そこで、Linq to XMLではDescendantsでエレメント名を指定すれば木構造をたどらずにダイレクトに指定する事が出来ます。(Descendantの意味は子孫)

XDocument rss = XDocument.Load(@"http://www.isisaka.com/blog/index.xml");
//Descendantsを使う方法
var query = from x in rss.Descendants("item")
            select x;
foreach (var i in query) {
    DateTime pubDate = DateTime.Parse(i.Element("pubDate").Value);
    Console.WriteLine(pubDate.ToString() + "\t: " + i.Element("title").Value);
}

先ほどと変わったのは3行目です。かなりスッキリ書けるようになったのがわかると思います。

2009/05/14 10:47:36     : プログラム言語 Axum
2009/05/11 19:03:02     : 第2回静岡 IT Pro 勉強会のご案内
2009/05/10 21:53:00     : 第30回 NT-Committee2関東勉強会
2009/05/10 21:21:41     : プリンタを買いました。(HP C309a)
2009/05/10 9:57:29      : キヤノン インクジェットプリンタのヘッド交換ができないので買い換えてやる!
2009/05/10 8:20:50      : そろそろCorei7かな。。
2009/05/10 0:06:41      : デザイナー目線でのFlash vs. Silverlight(2)
2009/05/09 15:42:19     : Linq to XML 入門その1 (XML文書の作り方)
2009/05/08 12:16:11     : Japan Windows Server : Hyper-V Server R2 Release Candidate 公開
2009/05/08 7:34:02      : Windows 7 向けリモートサーバ管理ツール
2009/05/06 23:34:56     : exFATはやはりブートメディアを作ることはできない
2009/05/05 15:48:55     : Vistaの供給は2011年1月まで、メインストリームサポートは2012年4月までらしい
2009/05/05 15:12:37     : Windows 7 RC アメリカでは一般公開
2009/05/04 23:32:40     : Windows 7(x64) をVirtualBoxに入れてみた。
2009/05/02 6:43:33      : プログラミングに適したフォント

表示結果はこうなります。

それでは、where句を使った条件検索を試してみます。

以下のコードでは5/11以降に更新された記事のリストを表示するようにします。

XDocument rss = XDocument.Load(@"http://www.isisaka.com/blog/index.xml");
//Descendantsを使う方法
var query = from x in rss.Descendants("item")
            where DateTime.Parse(x.Element("pubDate").Value) > DateTime.Parse("2009/5/11")
            select x;
foreach (var i in query) {
    DateTime pubDate = DateTime.Parse(i.Element("pubDate").Value);
    Console.WriteLine(pubDate.ToString() + "\t: " + i.Element("title").Value);
}

4行目のwhere句で5/11以降のデータのみ抽出するようにしています。

以下が実行結果です。

2009/05/14 10:47:36     : プログラム言語 Axum

11日以降に投稿した記事のリストが表示されています。

今回はここまで。

RSSを加工したい事はよくあるので、少しぐらいは参考になったでしょうか。

次回はもう少し複雑なクエリについて説明したいと思っています。ただ、データを作るのが難儀なのでしばらくお待ちください。。。

Linq to XML 入門その1 (XML文書の作り方)

準備

これから数回に分けてLinq to XMLの基礎的な使い方についてまとめていきます。

これらの記事に書かれているコードは特に明記がない場合は以下の環境で試験をしています。

  • Visual Studio Team Systeme 2008 Team Suite SP1
  • .NET Framework 3.5 SP1
  • Windows Vista Ultimate x64 ENU

また特に断らない限り使用するプロジェクトテンプレートはコンソールアプリケーションです。

Visual StudioはTeam Suiteを使っていますが、記事に書かれている内容はC# Express Editionでも問題ないはずです。

注意点

これからの記事では入門と言っておきながら、Linqの詳細な仕様や、XML自体の説明はしません。

Linq to XML各クラスの細かい説明はせずに、実際に最低必要だと思う事のみコードと説明をしていくので、各クラスが持つメソッドやメンバーの詳細についてはMSDNライブラリを参照してください。忘れなければ記事中に適宜リンクしていきます。

まずはこれが無くっちゃ始まらない

using System.Xml.Linq;

忘れずにusing句でLinq to XMLの名前空間を呼び出しておきましょう。また、Visual Studioのソリューションエクスプローラで参照設定を確認し、System.XML.Linqの参照設定がない場合には追加します。

最も単純例

最も簡単にXMLのデータを作成し、コンソールに表示させたいと思います。

var x = new XElement("Name", "Tadahiro Ishisaka");
Console.WriteLine(x);

ここでは単純にNameという1種類のエレメントを持つXMLデータを作成し、コンソールに表示させています。

XElementというクラスがLinq to XMLではXMLエレメントを表すクラスになります。

ちなみにLinq to XMLの各クラスは頭にXがつきます。

出力結果はこうなります。

Tadahiro Ishisaka

次にXMLのエレメントに属性(Attribute)を追加してみます。

var x = new XElement("Name", new XAttribute("ID", 99999), "Tadahiro Ishisaka");
Console.WriteLine(x);

今度はXElemntクラスのコンストラクタの中でXAttributeクラスを初期化し、IDという属性とその値として99999を追加しています。

出力結果はこうなります。

Tadahiro Ishisaka

NameエレメントにID属性が追加され、その値が99999となっているのがわかると思います。

また、このコードはXElementクラスのAddメソッドを使用して、以下のように記述しても同じ結果になります。

var x = new XElement("Name", "Tadahiro Ishisaka");
x.Add(new XAttribute("ID", 99999));
Console.WriteLine(x);

ここまでで、今まで.NETでXMLデータを処理されてきたかたには、かなりコーディング量が減って、楽にコードを書けていることがわかると思います。

Continue reading Linq to XML 入門その1 (XML文書の作り方)