studio Odyssey




スタジオ日誌

日誌的なもの

2011.12.31

「C#でMix-inってできねーの?」と言われた

Written by
しゃちょ
Category
C#

 この記事は大分古いので、C# 8.0以降が使える環境であれば(.net core 3.0以降)、interfaceのデフォルト実装を調べた方が幸せになれます。


 「C#でMix-inってできねーの?」と言われた。

 C#は、多重継承とか、Mix-inみてーな事はできねー言語なんだよ!言語作成者がそういう事やらせたくないって思ってるからな!

 なんだよ、つかえねー!と思われると嫌なので、「Mix-inっぽいことなら出来る」と答えておいた。ぽいことは出来る。C#3.0以降になっちゃうけど、そろそろ、新規でC#2.0の仕事はないだろ。

 というわけで、C#でMix-inぽいことに挑戦してみた。というのも、「できる」とは知っていても、使ったことがなかったから。だって、必要になったことないもん!

 論よりコード。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace MixIn
{
    /// <summary>
    /// このインターフェイス自体に意味はあんまりない
    /// </summary>
    public interface IMixIn
    {
    }

    /// <summary>
    /// インターフェイスの拡張メソッドになるクラス
    /// </summary>
    public static class MixInExtension
    {
        public static string GetHoge(this IMixIn obj)
        {
            return "Hoge";
        }
    }

    /// <summary>
    /// インターフェイスを実装しているので、拡張メソッドが使える
    /// </summary>
    public class Hoge : IMixIn
    {
    }

    /// <summary>
    /// これでもいい
    /// </summary>
    public class Hage : IMixIn
    {       
    }

    class Program
    {
        static void Main(string[] args)
        {
            Hoge h = new Hoge();
            
            Console.WriteLine(h.GetHoge());

            Hage g = new Hage();

            Console.WriteLine(g.GetHoge());

#if DEBUG
            Console.Read();
#endif
        }
    }
}

 ほら、Mix-inっぽいこと出来た。

 くっついているのはインターフェイスなので、インターフェイスが公開するメソッドだとかプロパティだとかにはアクセス可能なので、うまく使えば、綺麗な多態を表現できる。とはいえ、親クラスを作るのとどう違うんだって言う気もしないでもないが、こいつの利点は、インターフェイスに対しての拡張なので、「クラスは1個しか継承出来ないが、インターフェイスはいくつもつけられる」というC#の決まりからすると、結構、重宝しそうなテクニックだ。

 こうなると、ますます親クラスにパブリックなメソッドって必要なくなってくるよなぁ...

 しかし、ここで疑問に思った。インターフェイスが2つ以上つけられるって事は、多重継承問題と同じ問題でるんでね?

 ということで書いてみた。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace MixIn
{
    /// <summary>
    /// このインターフェイス自体に意味はあんまりない
    /// </summary>
    public interface IMixIn
    {
    }

    /// <summary>
    /// インターフェイスの拡張メソッドになるクラス
    /// </summary>
    public static class MixInExtension
    {
        public static string GetHoge(this IMixIn obj)
        {
            return "Hoge";
        }
    }

    /// <summary>
    /// 仮にこうしたらどうなる?
    /// </summary>
    public interface IMixIn2
    {
    }

    /// <summary>
    /// こう拡張する
    /// </summary>
    public static class MixIn2Extension
    {
        public static string GetHoge(this IMixIn2 obj)
        {
            return "Hoge2";
        }
    }

    /// <summary>
    /// インターフェイスを実装しているので、拡張メソッドが使える
    /// </summary>
    public class Hoge : IMixIn, IMixIn2
    {
    }

    /// <summary>
    /// これでもいい
    /// </summary>
    public class Hage : IMixIn
    {
    }

    class Program
    {
        static void Main(string[] args)
        {
            Hoge h = new Hoge();
            
            Console.WriteLine(h.GetHoge());

            Hage g = new Hage();

            Console.WriteLine(g.GetHoge());

#if DEBUG
            Console.Read();
#endif
        }
    }
}

 結論から言って、これ、コンパイル通らない。よくできてんなー。

 じゃあ、これは。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace MixIn
{
    /// <summary>
    /// このインターフェイス自体に意味はあんまりない
    /// </summary>
    public interface IMixIn
    {
    }

    /// <summary>
    /// インターフェイスの拡張メソッドになるクラス
    /// </summary>
    public static class MixInExtension
    {
        public static string GetHoge(this IMixIn obj)
        {
            return "Hoge";
        }
    }

    /// <summary>
    /// じゃあ、こうは?
    /// </summary>
    public interface IMixIn2 : IMixIn
    {
    }

    /// <summary>
    /// こう拡張する
    /// </summary>
    public static class MixIn2Extension
    {
        public static string GetHoge(this IMixIn2 obj)
        {
            return "Hoge2";
        }
    }

    /// <summary>
    /// インターフェイスを実装しているので、拡張メソッドが使える
    /// </summary>
    public class Hoge : IMixIn2
    {
    }

    /// <summary>
    /// これでもいい
    /// </summary>
    public class Hage : IMixIn
    {
    }

    class Program
    {
        static void Main(string[] args)
        {
            Hoge h = new Hoge();
            
            Console.WriteLine(h.GetHoge());

            Hage g = new Hage();

            Console.WriteLine(g.GetHoge());

#if DEBUG
            Console.Read();
#endif
        }
    }
}

 これは、想像通りの動きをする。インターフェイスがIMixIn2なので、IMixIn2のstaticなメソッドが呼び出される。実際の動きは違うけれど、オーバーライドというか、newされていると考えればいいだろう。まぁ、これは仕様だよね。ちょっと間違いの元になりそうだけど。

 このブログで一番閲覧数が多いのは、実はC#のコード関係です。

 サンプルコードがフルで乗ってるからかなぁ。


トラックバックURL

http://blog.studio-odyssey.net/cgi-bin/mt/mt-tb.cgi/736


コメントする