読者です 読者をやめる 読者になる 読者になる

いろいろ備忘録日記

主に .NET と Python絡みのメモを公開しています。

C#4.0の追加機能-01(dynamic-01, 動的プログラミング, dynamic programming, duck typing, late binding, C# 4.0)


2010年になった事なので、C# 4.0の追加機能を少しメモしようと思います。


現在Beta版が提供されているVisualStudio2010ですが、Visual C#も当然バージョンが
あがっています。次のバージョンはC# 4.0です。


今回は、そのC# 4.0の追加機能をちょこちょこメモしていきます。


ネタ元としているのは、以下のページ

からダウンロードできる。C# 4.0ホワイトペーパー (January 10)です。


C# 4.0の主要テーマは

動的プログラミング (Dynamic Programming)

となっています。なので、今回の追加機能もそれに関連する機能が追加されています。


大項目は以下の4つです。

  • Dynamic lookup
    • オブジェクトを動的に扱う機能
  • Named And Optional parameters
    • VBと同じレベルの名前付き引数とオプショナル引数
  • COM interop features
    • C#にて、COMを扱いやすくする機能 (Dynamic lookupとNamed And Optional parameters)
  • Variance
    • 共変性/反変性ともいいます。ジェネリックに対する追加機能です。


今回は、Dynamic lookupについてです。


C# 4.0で、以下のキーワード(型)が追加されました。

dynamic


文字通り動的なオブジェクトを表すものです。
以下のようにすると

dynamic dynObj = GetDynamicObject();

変数dynObjは動的なオブジェクトとして扱われ、コンパイル時の型チェックが
行われません。実際に型の解決が発生するのはランタイム時になります。


C# 3.0で追加されたvarと似ていますが、こちらはあくまで型の推論です。
なので、型のチェックがされますが、dynamicの方はコンパイル時のチェックが
行われません。


また、varとは違い、dynamicは引数の型や戻り値の型としても利用できます。

public dynamic GetDynamicObject(dynamic paramter) {
    ....
}

なんていうメソッドも定義可能です。


また、ジェネリックの型引数としてももちろん利用できます。

List<dynamic> dynList = new List<dynamic>();


内部的には、dynamicとして定義された値はコンパイル時に
object型に変換されます。なので、実行時にdynamic型というのは
存在しません。


dynamicが追加されたことにより以下の点が行えるようになりました。

  1. 遅延バインディング (late binding)
  2. ダックタイピング (duck typing)


late bindingはいいとして、pythonrubyなどでよく言われる
duck typingが出来るようになるのは面白いですね。
duck typingとは、ざっくりと書くと以下のような事です。

どんなインターフェースや親クラスを実装/継承しているのかに関係無く
特定の機能(メソッドやプロパティなど)を持っているものはみんな同じ
ものとして扱う。

(アヒルかどうかは分からないけど、アヒルのような鳴き声をしていたり
 アヒルのような歩き方をしている場合、アヒルとして扱う)

以下のような感じです。

class A {
    public void Execute() {
        ...
    }
}

class B {
    public void Execute() {
        ...
    }
}

//
// クラスAとBはそれぞれ、親クラスも共有しておらず、特定のインターフェースも
// 実装していない。でも、Executeという名前のメソッドは持っている。
// なら同じ仲間のものとして扱う.
//
new List<dynamic>{ new A(), new B() }.ForEach(dynObj => dynObj.Execute() );


実際、実務レベルでdynamicを多用するかというと、多分あまり使われないだろうな
というのが個人的な感想です。ただ、COM Interopでは大活躍で、今までC#では
面倒だった部分が大分書きやすくなります。
どんな風になるのかは、下記のサンプルを参照お願いします。


以下、サンプルです。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Excel = Microsoft.Office.Interop.Excel;

namespace DynamicLookup01 {

    class Program {

        static void Main(string[] args) {
            (new Program()).Execute();
        }

        private void Execute() {
            //
            // dynamicのサンプル.
            //
            // dynamicを利用すると、文字通りオブジェクトを動的に扱う事が
            // 出来るようになる。
            //
            // 変数として利用するだけではなく、戻り値や引数の型としても利用できる。
            //
            // C# 3.0で追加されたvarとの違いに注意。
            // varは、あくまでも型の推論であり、コンパイル時に問題は解決される。
            //
            // varとは異なり、dynamicを利用する場合、コンパイル時に型の問題は
            // 無視され、ランタイム時に解決が行われる。
            // (つまり、間違ったメソッド名を指定している場合は実行時にエラーが発生することになる。)
            // (内部的には、コンパイル時にobject型とCallSiteに変換される模様。なので実行時にdynamic型というのは存在しない。)
            //
            // dynamicが効果を発揮するのは、COMを利用する処理やPythonなどの動的言語とやり取りを行う場合
            // または、リフレクションなど、コンパイル時に型がはっきりしない場合に有効となる。
            //
            // 以下の例では、サンプルとしてOperationクラスを作成し、それをFactoryクラス経由でdynamic型、object型, Operation型として
            // 取得するようにしている。呼び元(Program.Execute)では、dynamicで受け取り、動的にメソッドをコール
            // している。
            //
            dynamic operation1 = OperationFactory.GetOperationAsDynamic();
            dynamic operation2 = OperationFactory.GetOperationAsObject();
            dynamic operation3 = OperationFactory.GetOperation();

            new List<dynamic>{ operation1, operation2, operation3 }.ForEach(dynObj => dynObj.PrintHelloWorld());

            //
            // dynamic型を引数として公開しているメソッドを呼んでみる.
            //
            operation1.PrintStrings("HELLO DYNAMIC WORLD");
            (new Operation()).PrintStrings("HELLO DYNAMIC WORLD");

            //
            // COMの呼び出し.
            //
            // Excel操作を行っている。PIAにもdynamicが設定されており
            // 以下の例でいうと、Excel.WorkBook.Addメソッドの戻り値はdynamicとなっている。
            //
            // これにより、今までは
            //     ((Excel.Range) workSheet.Cells[1, 1]).Value = "HELLO DYNAMIC WORLD";
            // のようにしなければいけなかった部分が
            //     workSheet.Cells[1, 1].Value = "HELLO DYNAMIC WORLD";
            // と記述できるようになる。
            //
            // また、Excel操作中に呼び出している各メソッドにてType.Missingが見あたらないのは
            // C# 4.0から追加されたオプショナル引数と名前付き引数のおかげである。
            //
            Excel.Application app = new Excel.Application();
            app.Visible = true;

            Excel.Workbook workBook = app.Workbooks.Add();
            dynamic workSheet = workBook.Sheets.Add();

            workSheet.Cells[1, 1].Value = "HELLO DYNAMIC WORLD";

            Console.WriteLine("起動されたExcelを確認すると文字列が挿入されています。キーを押下すると処理を進めます。");
            Console.ReadLine();
            app.Quit();

            //
            // 上記の処理と同じ内容をExcel.Application経由で行う.
            //
            var excel = new Excel.Application();

            excel.Visible = true;
            excel.Workbooks.Add();
            
            excel.Cells[1, 1].Value = "HELLO DYNAMIC WORLD";

            Console.WriteLine("起動されたExcelを確認すると文字列が挿入されています。キーを押下すると処理を進めます。");
            Console.ReadLine();
            excel.Quit();
            
            Console.WriteLine("Press any key to exit...");
            Console.ReadLine();
        }

        #region Sample Classes
        class OperationFactory {

            public static dynamic GetOperationAsDynamic() {
                return new Operation();
            }

            public static object GetOperationAsObject() {
                return new Operation();
            }

            public static Operation GetOperation() {
                return new Operation();
            }
        }

        class Operation {

            public void PrintHelloWorld() {
                Console.WriteLine("Hello World");
            }

            public void PrintStrings(dynamic strings) {
                Console.WriteLine(strings);
            }
        }
        #endregion
    }
}