Visual Studio 2008 C#で開発効率アップ!

第4回C#3.0の生産力と仕組み

はじめに

前回までで、C#3.0の主な新機能については、一通りご説明してきました。 今回は、C#3.0の新機能を使って記述したサンプルコードをC#2.0でもご紹介してみます。 これによって、C#3.0の生産力を実際にご確認ください。

コード比較で理解するC#3.0の生産力

最初に、タイトルを除いてまったく同じ動作をしていることを示す、以下の二つの画面イメージを確認してみてください。 Form3、Form2という二つのフォームのそれぞれにlistBoxというリストボックスが配置されています。

C#3.0のコードで作成した結果の画面
C#3.0のコードで作成した結果の画面
C#2.0のコードで作成した結果の画面
C#2.0のコードで作成した結果の画面

どちらもリストボックスのアイテムをクリックすることで、その名前の番号をメッセージボックスに表示します。

画像

行っていることは、次の通りです。

  • 番号・名前・年齢プロパティを持つオブジェクトの作成
  • 各オブジェクトをアイテムとしたコレクションオブジェクトの作成
  • 番号・名前プロパティを持つオブジェクトのデータソースに作り変え
  • 同時に、未成年情報だけを抽出して名前順に並べ替え

Form3、Form2という二つのフォームの画面イメージとサンプルコードです。

C#3.0
using System.Linq;
using System.Windows.Forms;
namespace WindowsFormsApplication1 {
    public partial class Form3 : Form {
        public Form3() {
            InitializeComponent();

            var c = new[] {
                new { Code = 1, Name = "Tanaka" , Age = 18 },
                new { Code = 2, Name = "Suzuki" , Age = 32 },
                new { Code = 3, Name = "Sato"   , Age = 19 },
                new { Code = 4, Name = "Yamada" , Age = 12 },
                new { Code = 5, Name = "Aoyama" , Age = 21 }
            };

            var q = 
                from x in c 
                where x.Age < 20
                orderby x.Name
                select new { Code = x.Code, Name = x.Name };

            listBox.DataSource = q.ToArray();
            listBox.ValueMember = "Code";
            listBox.DisplayMember = "Name";
            listBox.SelectedValueChanged += (sender, e) => {
                this.Value = listBox.SelectedValue;
                MessageBox.Show(this.Value.ToString()); };
        }

        public object Value { get; private set; }
    }
}
C#2.0
using System;
using System.Collections.Generic;
using System.Windows.Forms;
namespace WindowsFormsApplication1 {
    public partial class Form2 : Form {
        public Form2() {
            InitializeComponent();

            Member tanaka = new Member();
            tanaka.Code = 1;
            tanaka.Name = "Tanaka";
            tanaka.Age = 18;

            Member suzuki = new Member();
            suzuki.Code = 2;
            suzuki.Name = "Suzuki";
            suzuki.Age = 32;

            Member sato = new Member();
            sato.Code = 3;
            sato.Name = "Sato";
            sato.Age = 19;

            Member yamada = new Member();
            yamada.Code = 4;
            yamada.Name = "Yamada";
            yamada.Age = 12;

            Member aoyama = new Member();
            aoyama.Code = 5;
            aoyama.Name = "Aoyama";
            aoyama.Age = 21;

            Member[] c = new Member[] {
                tanaka, suzuki, sato, yamada, aoyama };

            List<Item> list = new List<Item>();
            foreach (Member x in c) {
                if (x.Age < 20) {
                    Item item = new Item();
                    item.Code = x.Code;
                    item.Name = x.Name;
                    list.Add(item);
                }
            }
            list.Sort(
                delegate(Item a, Item b) {
                    return string.Compare(a.Name, b.Name); });

            listBox.DataSource = list;
            listBox.ValueMember = "Code";
            listBox.DisplayMember = "Name";

            listBox.SelectedValueChanged += 
                delegate(object sender, EventArgs e) {
                    this.Value = this.listBox.SelectedValue;
                    MessageBox.Show(this.Value.ToString()); };
        }

        private object _Value;
        public object Value {
            get { return this._Value; }
            private set { this._Value = value; }
        }
    }

    public class Item {
        private int _Code;
        public int Code {
            get { return this._Code; }
            set { this._Code = value; }
        }

        private string _Name;
        public string Name {
            get { return this._Name; }
            set { this._Name = value; }
        }
    }

    public class Member {
        private int _Code;
        public int Code {
            get { return this._Code; }
            set { this._Code = value; }
        }

        private string _Name;
        public string Name {
            get { return this._Name; }
            set { this._Name = value; }
        }

        private int _Age;
        public int Age { 
            get { return this._Age; }
            set { this._Age = value; }
        }
    }
}

もちろん、どちらも同じ処理をするコードです。 どちらが生産性が高いのかについては、言うまでもないでしょう。

LINQの正体

前回クエリー式の正体はシンタックスシュガーであることをご説明しました。 今回は前述のサンプルコードで使用しているLINQ to Objectsについて、もう少し掘り下げてご説明したいと思います。

まずは、次の実行結果の画面をご覧ください。

画像

listBox1~3は同じ内容が表示されているだけの単なるフォームです。 しかし、3つのリストボックスに表示されているデータソースは異なる処理を経て生成されています。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows.Forms;
namespace WindowsFormsApplication1 {
    public partial class FormS : Form {
        public FormS() {
            InitializeComponent();

            var c = new[] {
                new { Code = 1, Name = "Tanaka" , Age = 18 },
                new { Code = 2, Name = "Suzuki" , Age = 32 },
                new { Code = 3, Name = "Sato"   , Age = 19 },
                new { Code = 4, Name = "Yamada" , Age = 12 },
                new { Code = 5, Name = "Aoyama" , Age = 21 }
            };

            var q1 = 
                from x in c 
                where x.Age < 20
                orderby x.Name 
                select new { Code = x.Code, Name = x.Name };

            var q2 = c
                .Where(x => x.Age < 20)
                .OrderBy(x => x.Name)
                .Select(
                    x => new { Code = x.Code, Name = x.Name });

            var q3 = c
                .条件式(x => x.Age < 20)
                .整列(x => x.Name)
                .選択(
                    x => new { Code = x.Code, Name = x.Name });

            listBox1.Binding(q1.ToArray(), "Code", "Name");
            listBox2.Binding(q2.ToArray(), "Code", "Name");
            listBox3.Binding(q3.ToArray(), "Code", "Name");
        }
    }
    public static class Extensions {
        public static void Binding(
            this ListControl x, object dataSource, 
            string valueMember, string displayMember)
        {
            x.DataSource = dataSource;
            x.ValueMember = valueMember;
            x.DisplayMember = displayMember;
        }
        
        public static IEnumerable<T> 条件式<T>(
            this IEnumerable<T> value, Func<T, bool> func) 
        {
            var r = new List<T>();
            foreach(var x in value) if (func(x)) r.Add(x);
            return r;
        }
        
        public static IEnumerable<TR> 選択<TS, TR>(
            this IEnumerable<TS> value, Func<TS, TR> func)
        {
            var r = new List<TR>();
            foreach(var x in value) r.Add(func(x));
            return r;
        }
        
        public static IEnumerable<TS> 整列<TS, TK>(
            this IEnumerable<TS> value, Func<TS, TK> func)
                where TK : IComparable 
        {
            var a = value.ToArray();
            for(var i = 0; i < a.Length; ++i) {
                var min = i;
                for(var j = i + 1; j < a.Length; ++j) {
                    if (func(a[j]).CompareTo(func(a[min])) < 0) {
                        min = j;
                    }
                }
                if (i != min) {
                    var t = a[min];
                    a[min] = a[i];
                    a[i] = t;
                }
            }
            return a;
        }
    }
}

最初に、基本となるデータソースを作ります。 そして、q1~3にデータソースを格納しています。

q1はクエリー式を使ってデータソースを生成しています。
q2は、q1をシンタックスシュガーを使わずに生成しています。
q3は、LINQによって行われている処理と同様の処理を自作したものを使用しています。

この結果、出来上がったデータソースは同じものになります。

最後に、ListBoxにBindingという拡張メソッドを作り、これを使ってlistBox1~3にq1~3のデータソースをセットしています。

既にご紹介したC#3.0の新機能を駆使することで、LINQで行われている処理と同様のものが自作できてしまうことがわかるでしょう。 同時に、C#3.0の新機能はLINQのためにあると一般的に言われている理由も納得できるかと思います。

型推論や拡張メソッド、ラムダ式がどのように使われているのかも、上記のコードを見れば既に理解できるはずです。

最後に

今回のC#の新機能は如何でしたか?

私は、今回のC#の新機能を初めて知った時に、一人の開発者として純粋に感動しました。 そして、実際に使ってみて、コーディングの楽しさに喜び、そして生産力の高さに驚きました。

この特集を読んでくださった皆さんにも、私と同じような感動を味わっていただけることを願いながら、本特集は終了とさせていただきたいと思います。

最後に、本連載を読んでくださった皆さん、連載の機会を与えてくださった技術評論社と担当の方々を始めとする多くの皆さんに心からお礼を申し上げます。

ありがとうございました。

おすすめ記事

記事・ニュース一覧