DLR を使った Excel プログラミング

Visual Studio 2010ベータ1のウォークスルーの中に Office プログラマビリティ があります。このウォークスルーの中で、1か所だけ 動的呼び出しになると記述されたところがあります。この個所を、このウォークスルーではNo PIA(埋め込みPIA)というシナリオを確認するために、最終的には、キャストします。なぜキャストするかといえば、埋め込みPIAでは必要な型情報のみを取り込むからだと説明されています。この内容は、さておきNo PIAにしないコードの抜粋を以下に引用します。

 public class Account
{
    public int ID { get; set; }
    public double Balance { get; set; }
}

using Excel = Microsoft.Office.Interop.Excel;
class Program
{
    static void Main(string[] args)
    {
        var checkAccounts = new List<Account> {
            new Account { ID = 345, Balance = 541.27 },
            new Account { ID = 123, Balance = -127.44 } };
    
        DisplayInExcel(checkAccounts, (account, cell) =>
        {   cell.Value2 = account.ID;
            cell.get_Offset(0, 1).Value2 = account.Balance;
            if (account.Balance < 0)
            {   cell.Interior.Color = 255;
                cell.get_Offset(0, 1).Interior.Color = 255;
            }
        });
    }

    public static void DisplayInExcel(
           IEnumerable<Account> accounts,
           Action<Account, Excel.Rage> DisplayFunc)
    {
        var xl = new Excel.Application();
        xl.Workbooks.Add();
        xl.Visible = true;
        xl.get_Range("A1").Value2 = "ID";
        xl.get_Range("B1").Value2 = "Balance";
        xl.get_Range("A2").Select();
        foreach (var ac in accounts)
        {   DisplayFunc(ac, xl.ActiveCell);
            xl.ActiveCell.get_Offset(1, 0).Select();
        }
        xl.get_Range("A1:B3").Copy();

        xl.Columns[1].AutoFit();
        xl.Columns[2].AutoFit();
    }
}

xl.Columns[1].AutoFit(); 」が実行時に解決されて、AutoFitメソッドが呼ばれます。この実行時に解決するというのは、xl.Columns[1]が実行時にRangeオブジェクトを返すために、AutoFitメソッドを呼び出せるようになるという意味です。このウォークスルーでは埋め込みPIAを確認するためですが、最終的に「((Excel.Range) xl.Columns[1]).AutoFit();」とキャストを追加するようになります。

 しかし、.NET Framework 4.0ベータ1には動的呼び出しをサポートするために DLRが含まれています。DLRを使うようにすることで、埋め込みPIAどころか、型情報自体を実行時に解決することができます。そのように改造した Programクラスを以下に示します。

 //using Excel = Microsoft.Office.Interop.Excel;
class Program
{
    static void Main(string[] args)
    {
        var checkAccounts = new List<Account> {
            new Account { ID = 345, Balance = 541.27 },
            new Account { ID = 123, Balance = -127.44 } };
    
        DisplayInExcel(checkAccounts, (account, cell) =>
        {   cell.Value2 = account.ID;
            cell.Offset(0, 1).Value2 = account.Balance;
            if (account.Balance < 0)
            {   cell.Interior.Color = 255;
                cell.Offset(0, 1).Interior.Color = 255;
            }
        });
    }

    public static void DisplayInExcel(
           IEnumerable<Account> accounts,
           Action<Account, dynamic> DisplayFunc)
    {
        dynamic xl = GetComInstance("Excel.Application");
        xl.Workbooks.Add();
        xl.Visible = true;
        xl.Range("A1").Value2 = "ID";
        xl.Range("B1").Value2 = "Balance";
        xl.Range("A2").Select();
        foreach (var ac in accounts)
        {   DisplayFunc(ac, xl.ActiveCell);
            xl.ActiveCell.Offset(1, 0).Select();
        }
        xl.Range("A1:B3").Copy();

        xl.Columns[1].AutoFit();
        xl.Columns[2].AutoFit();
    }
    // COMインスタンス作成用のヘルパーメソッド
    public static dynamic GetComInstance(string progID)
    {   Type comType = Type.GetTypeFromProgID(progID);
        return System.Activator.CreateInstance(comType);
    }
}

プロジェクトからPIAアセンブリへの参照を削除して、ビルドすれば DLR を活用した動的呼び出しが完成します。C#では、PIAを使っている時にGet_RangeやGet_Offsetというメソッドだったことにも注意してください。VBと同じように「Get_」プレフィックスのつかないメソッドで呼び出せるようになります。これは、dynamicキーワードによってC# Binderが DLR のCOM呼び出し(VB6のレイトバインディングと同等)を呼び出すからです。

VB のウォークスルーのコードも、型名を Object に変更すれば、同じように動的呼び出しを実現することができます。VBの場合は、VBコンパイラがVB Binderである NewLateBindingクラスを利用するコードを出力することで実現しています。

PIAがあれば実行時の型変換の規則がありますので実行速度的には良いでしょうが、DLRを使った動的呼び出しも私には良さそうに思えます。皆さんは、どちらがお好きでしょうか?

追記:VBではNewLateBindingクラスが活躍しますが、DLRに対する呼び出しなどは IDOBinderクラスとIDOUtilsクラスを使って処理しているようです。C#では、CTPの時と違いMicrosoft.CSharp.dllアセンブリにRuntimeBinderネームスペースが存在しています。