2009年6月7日日曜日

Visual Studio 2010 beta1 C++でのinclude

なんでもVC++が進化しているそうなので試してみたく。
プログラムを書こうとして#include <...>まで書いて気がついた。はてさて、header fileのdirectoryはどう設定するの?

Visual Studio 2008までは ツール - オプション - プロジェクトおよびソリューション - VC++ディレクトリ という設定項目があり、そこにディレクトリを入力できた。が、2010 beta1ではそこがなくなっている。
もちろんプロジェクトのプロパティに設定してもコンパイルできることにはできるが、システムにインストールしたheader fileのdirectoryをプロジェクトごとに書くのはおかしい。

プロジェクトファイルを参照したところ答えがあった。

<ImportGroup Label="PropertySheets">
<Import Project="$(LocalAppData)\Microsoft\VisualStudio\10.0\Microsoft.Cpp.$(Platform).user.props"
Condition="exists('$(LocalAppData)\Microsoft\VisualStudio\10.0\Microsoft.Cpp.$(Platform).user.props')" />
</ImportGroup>
つまり$(LocalAppData)\Microsoft\VisualStudio\10.0\Microsoft.Cpp.$(Platform).user.propsに書けば取り込まれます。

2009年6月5日金曜日

C# 4.0 dynamic その2

というわけでサンプルコードを使って、C# 3.0とC# 4.0とでどのように変わってくるかを説明します。
サンプルは、Outlook 2007の受信トレイ以下のフォルダにある重複メール(Message-Idが一致するメール)をゴミ箱へ移動するコードです。Outlook 2007の新機能Tableオブジェクトを使用しているのでOutlook 2003以前では動作しません。

まずはC# 3.0でのコード

static class Program {
// Outlookを初期化し、受信トレイに対してVisit()を呼びます。
static void Main( string[] args ) {
var application = new Application();
Visit( application.Session.GetDefaultFolder( OlDefaultFolders.olFolderInbox ) );
}

// 各フォルダに対してProcess()を呼びます。また再帰的にフォルダを探索していきます。
static void Visit( MAPIFolder folder ) {
Process( folder );
foreach( MAPIFolder f in folder.Folders )
Visit( f );
}

// ちょっとだけ読みやすくなる拡張メソッドです。
static IEnumerable<Row> AsEnumerable( this Table table ) {
while( !table.EndOfTable )
yield return table.GetNextRow();
}

// 本題の処理です。Message-Id一覧を作成し、重複するものがあったらゴミ箱へ移動します。
static void Process( MAPIFolder folder ) {
var messageIds = new HashSet<string>();
var table = folder.GetTable( null, OlTableContents.olUserItems );
table.Columns.RemoveAll();
table.Columns.Add( "http://schemas.microsoft.com/mapi/proptag/0x1035001E" );
table.Columns.Add( "EntryID" );
var ns = folder.Session;
var trash = ns.GetDefaultFolder( OlDefaultFolders.olFolderDeletedItems );
foreach( var row in table.AsEnumerable() )
if( !messageIds.Add( (string)row[1] ) ) {
MailItem mailItem = (MailItem)ns.GetItemFromID( (string)row[2], null );
Console.WriteLine( "{0}", mailItem.Subject );
mailItem.Move( trash );
}
}
}

続いてC# 4.0でのコード。違いがあるのはProcess()メソッドだけです。
static void Process( MAPIFolder folder ) {
var messageIds = new HashSet<string>();
var table = folder.GetTable( TableContents: OlTableContents.olUserItems );
table.Columns.RemoveAll();
table.Columns.Add( "http://schemas.microsoft.com/mapi/proptag/0x1035001E" );
table.Columns.Add( "EntryID" );
var ns = folder.Session;
var trash = ns.GetDefaultFolder( OlDefaultFolders.olFolderDeletedItems );
foreach( var row in table.AsEnumerable() )
if( !messageIds.Add( row[1] ) ) {
MailItem mailItem = ns.GetItemFromID( row[2] );
Console.WriteLine( "{0}", mailItem.Subject );
mailItem.Move( trash );
}
}
まず、デフォルト引数に対応したため、本質的でないnull引数を記述しなくてもよくなっています。

次に、row[1]とrow[2]の実体はstring型ですが宣言はobject型でした。C# 4.0ではここがdynamic型になっているため、明示的なキャストなしでメソッド引数に使えます。
ns.GetItemFromID()の戻り値もobjectがdynamicに変わっています。実体はCOMオブジェクトです。dynamicのまま扱ってもSubjectプロパティやMove()メソッドは呼び出せますが、MailItem型ということがわかっていますのでキャストしています。その際にも明示的にキャスト式を書く必要はありません。

2009年6月4日木曜日

C# 4.0 dynamic

Visual Studio 2010 beta1で遊んでいます。
C# 4.0および.NET Framework 4.0で提供されるdynamicキーワードについて誤解していました。

dynamic宣言した変数はメソッド呼び出しを行っても、実行時にバインドされます。
ここまでは合ってます。

誤解していたのは、ソースコード上で明示的に宣言したもののみがdynamic変数になると考えていました。(object→dynamicにキャストするイメージでした。)しかし、Visual Studio 2010で試していてわかったのですが、PIAなどのクラスメソッドの戻り値がそもそもdynamicになっています。(C# 3.0まではobject型)
なので、今までstringなどの変数に代入するためにはobject→stringの明示的なキャストが必要でした。C# 4.0ではdynamicになっているため、stringに代入しようとするだけで自動的にキャストされます。C# 3.0のvarで受けた場合、dynamicのままでした。

文字だけなのでイメージがわきづらいですが、PIAを使った場合に思った以上にdynamicの恩恵が受けられそうです。

サンプルコードでも書けばわかりやすいかな?