投稿数 - 437, コメント - 52933, トラックバック - 156

partial class と code snippet で Mix-in

プリプロセッサを作って C# 2.0 で Mix-in を実現しようという記事を .NET Expert #02 でやねうらおさんが書いていた。 フッと思い出したので、もっとお手軽にできないだろうかと考えてみた。

多重継承ができない言語に Mix-in が欲しいというのは当然の欲求だ。Mix-in がなければ無駄に同じようなコードを書き殴らないといけない。

Mix-in を知らない人は各自調べて欲しい。非常に便利である事が分かると思う。私が知っている Mix-in の実装系は Ruby だ。D 言語にも実装されている らしい。Mix-in を実装している言語は数多くある。

単純に言ってしまえば、Mix-in とは他のクラスの実装を取り込む事だ。これは通常は多重継承で可能だ。もちろん、多重継承は実装を取り込む事だけが目的ではないが。

C# 2.0 や VB 8.0 では多重継承が禁止されているので、Mix-in を使って実装を取り込まなければならないが、これらの言語は Mix-in もできない。「interface を使えば多重継承が可能じゃないか?」というのは違う。interface はあくまで「シグネチャの明言」であって実装を継承する事ができない。

しかし、よくよく考えれば C# 2.0 及び VB 8.0 から導入された partial class は Mix-in のように他の定義から実装を取り込んでいるように振る舞う。違いといえば、partial class はそのクラス専用になる事だ。

ところが、この partial クラスも「クラス名」の箇所だけテキスト置換できれば他のクラスにも適用できる。そして、Visual Studio 2005 から導入された code snippet はこの目的ににうってつけだ。

code snippet を Mix-in ライブラリとして使ってしまうと割り切れば、非常に簡単に Mix-in ライブラリを作る事が可能となる。

簡単な例を以下に挙げよう。

今回の Mix-in の機能は「Mix-in Comparable」だ。この Mix-inをクラスが取り込むと、クラスは比較演算子「<、<=、>、>=」を実装した事になる。以下が code snippet の定義だ。

<?xml version="1.0" encoding="utf-8"?>
<CodeSnippets xmlns="http://schemas.microsoft.com/VisualStudio/2005/CodeSnippet">
    <CodeSnippet Format="1.0.0">
        <Header>
            <Title>
                Mix-in Comparable
            </Title>
        </Header>
        <Snippet>
            <Declarations>
                <Literal>
                    <ID>Class</ID>
                    <ToolTip>Class 名。</ToolTip>
                </Literal>
            </Declarations>
            <Code Language="CSharp">
                <![CDATA[
    #region Mix-in Comparable
    partial class $Class$ : IComparable<$Class$>
    {
        public static bool operator <($Class$ lhs, $Class$ rhs)
        {
            if(lhs.CompareTo(rhs) < 0)
            {
                return true;
            }
            else
            {
                return false;
            }
        }
 
        public static bool operator <=($Class$ lhs, $Class$ rhs)
        {
            if(lhs.CompareTo(rhs) <= 0)
            {
                return true;
            }
            else
            {
                return false;
            }
        }
 
        public static bool operator >($Class$ lhs, $Class$ rhs)
        {
            if(lhs.CompareTo(rhs) > 0)
            {
                return true;
            }
            else
            {
                return false;
            }
        }
 
        public static bool operator >=($Class$ lhs, $Class$ rhs)
        {
            if(lhs.CompareTo(rhs) > 0)
            {
                return true;
            }
            else
            {
                return false;
            }
        }
    }
    #endregion
                ]]>
            </Code>
        </Snippet>
    </CodeSnippet>
</CodeSnippets>

Mix-in Comparable を取り込んでみよう。

public class Test
{
    private int _id;
 
    public int ID
    {
        get { return _id; }
        set { _id = value; }
    }
}

Test クラスの定義の下に「Mix-in Comparable」snippet を挿入する。

#region Comparable Mix-in
partial class Test : IComparable<Test>
{
    public static bool operator <(Test lhs, Test rhs)
    {
        if(lhs.CompareTo(rhs) < 0)
        {
            return true;
        }
        else
        {
            return false;
        }
    }
 
    public static bool operator <=(Test lhs, Test rhs)
    {
        if(lhs.CompareTo(rhs) <= 0)
        {
            return true;
        }
        else
        {
            return false;
        }
    }
 
    public static bool operator >(Test lhs, Test rhs)
    {
        if(lhs.CompareTo(rhs) > 0)
        {
            return true;
        }
        else
        {
            return false;
        }
    }
 
    public static bool operator >=(Test lhs, Test rhs)
    {
        if(lhs.CompareTo(rhs) > 0)
        {
            return true;
        }
        else
        {
            return false;
        }
    }
}
#endregion

ここまででコンパイルすると、「Test クラスを partial にしなさい。Test クラスで IComparable インターフェースの実装をしなさい」と言われるのでその通りにする。

public partial class Test
{
    private int _id;
 
    public int ID
    {
        get { return _id; }
        set { _id = value; }
    }
 
    public int CompareTo(Test other)
    {
        if(_id < other._id)
        {
            return -1;
        }
        else if(_id > other._id)
        {
            return 1;
        }
        else
        {
            return 0;
        }
    }
}

これで完成だ。code snippet のおかげで、この Mix-in Comparable はどのクラスにも取り込む事ができ、しかも非常に簡単にできる。

欠点は、言語ごとに snippet ファイルを用意しなければいけない点だ。

今回の実装はあまり良くない。等値演算子の扱いが CompareTo が 0 のとき true にするか、Equals が true のとき true にするかで曖昧になったので、等値演算子は無視したからだ。どちらにしろ、<、<= 等の比較演算子をオーバーロードするのはあまり宜しくないかもしれない。

機会があれば、様々な Mix-in snippet ライブラリを随時公開していきたい。

投稿日時 : 2006年10月8日 15:58

フィードバック

# LNhyNMqOkIRMXu

xJFh5p It's pleasant sitting at work to distract from it?to relax and read the information written here:D

# VlglxIflafSHsLKQ

Yeah, in my opinion, it is written on every fence!!...

# OckSCOsyIJABV

Of course, I understand a little about this post but will try cope with it!!...
2011/12/27 18:19 | http://www.laurenslinens.com

# dMKNpCehJOmCJ

I read and feel at home. Thanks the creators for a good resource..!

コメントの投稿

タイトル
名前
URL
コメント