C#にRubyにおけるmoduleに相当する機能はありますか?

投稿者: Anonymous

C#にRubyにおけるmoduleに相当する機能はありますか?
共通の処理をもたせるにはクラスの継承を利用するか、依存性の注入をするかのどちらかしかないでしょうか?

++追記
C#を昨日勉強し始めたばかりで、基本機能を把握する過程で疑問に思っただけで、特に具体的な問題に直面しているわけではありません。
Rubyのmoduleに相当する機能に期待することは、「関連の低いクラス間でメソッドの共有”のみ”を行うこと」です。
継承は基本的に関連するクラスで行うものですし、他の方法ですとわざわざインスタンス化する必要があったりするので
moduleがあればシンプルでいいな、と思った次第です。

今の所,クラスのインスタンス変数に共有するメソッドをまとめたstaticなクラスのクラスインスタンス?を入れておくのが、思いついてる中で一番目的に適った方法です。
=>
Rubyの感覚で変数にクラスオブジェクトを代入出来ると思ってましたが、c#では出来ないようですね。thisをstaticメソッド内で使用することも出来ず、returnしたりすることも出来ませんし。

++回答から
c#8.0でデフォルトインターフェースメソッドが実装されるのが一番ですが、現状では拡張メソッドを使った方法が一番目的に即してるようですね。staticメソッドとしての制約は受けますが、拡張メソッドの引数の型にインターフェースを指定すれば、特定のインターフェースを継承したクラスのみにメソッドの使用を許可出来るので、不用意な使用の回避も出来ますし。

解決

Rubyのモジュールを使う目的はだいたい次の三つです。

  1. 名前空間
  2. ユーティリティクラス
  3. Mixin

それぞれC#ではどうするのかを見ていきましょう。

名前空間として使う場合はそれほど難しくありません。C#ではnamespaceを使えばいいだけです。ただ、名前空間の構成の仕方が全く違いますので、名前を分けるのに使う機能として、それぞれモジュールやnamesapceを使うと考えてください。

ユーティリティクラスというのは、特定のオブジェクトやクラスから独立したメソッド類を集めた物です。Rubyではモジュールがその役目を担いますが、JavaやC#ではクラスで実装するためユーティリティクラスと言われます。Rubyでユーティリティクラスとしてモジュールを使うときは、module_functionを使ってメソッドをモジュール関数にします。C#の場合はstaticメソッドにします。RubyのMathとC#のSystem.Mathがこの使い方になりますが、Rubyの方はモジュールであり、C#の方はクラスで実装されています。

最後のMixinについてですが、これは共通のメソッドを通常の継承とは別に持たせる機能です。RubyのComparableEnumerableがこのMixin用に用意されたモジュールと言えます。Rubyではeachメソッドを適切に定義して、EnumerableをMixinさせると、mapselectと言ったメソッドを使うことができます。このように、特定のメソッドから派生したメソッドを個別に定義せずに、共通で定義しておいて、コードを簡潔にするという機能です。Mixinと同様の働きができる物として、trait(PHP等)や多重継承(C++、Python等)があります。多重継承が出来ない単一継承だからこそ、この機能があると言っても良いでしょう。

このMixinまたはそれと同等の機能についてですが、現在のバージョン(7.2)では、C#にはありません。ただ、ライバルであるJavaにはインターフェースデフォルトメソッドというMixin相当の機能が搭載されたため、将来同じような物が搭載される可能性はあります(私は可能性が低いと思っていますが)。

さて、RubyのEnumerable相当になりそうなものにC#にはSystem.Collections.Generic.IEnumerable<T>があります。こちらも同様に、GetEnumerator()メソッドを適切に定義すればSelect()Wherer()などがメソッドのようにつかえるようになります。あれ、結局はRubyのEnumerableと同じことができているじゃないかと思うでしょう。それは半分正解で半分間違いです。

これは拡張メソッドというC#の機能であり、拡張メソッド自体はただのstaticメソッドにすぎません。C#は呼び出しの方に工夫をしました。ユーティリティクラスを作るような形でstaticメソッドを作成するのですが、このとき拡張メソッドとして定義する(仮引数にthis修飾子をつける)とあたかもインスタンスメソッドのように呼び出せるようにしたのです。

using System;
public interface IIntNum
{
        int Value();
}
public static class IntNum
{
        public static int Double(this IIntNum num)
        {
                return num.Value() * 2;
        }
}
public class IntBox : IIntNum
{
        public int x;
        public int Value()
        {
                return this.x;
        }
}
public class MainClass
{
        public static void Main(string[] args)
        {
                var ib = new IntBox{x = 3};
                var x = IntNum.Double(ib); // 通常のstaticメソッド呼び出し
                Console.WriteLine(x); // 6
                var y = ib.Double(); // 拡張メソッドとしての呼び出し
                Console.WriteLine(y); // 6
        }
}

これは、Mixinやその他の類似した機能のように、インスタンスメソッドとしてそのクラスに追加されたわけではありません。見せかけ上インスタンスメソッドのように書けるというだけで、その本質はstaticメソッドのままです。しかし、実用上はMixin相当であり、十分であると考えても良いでしょう。

ということで、C#にはMixinまたはそれ相応の機能はありませんが、拡張メソッドを用いることで、見せかけ上同じようにできるとなります。(ただし、その仕組みは全く異なる物ですので、同じことが必ずできるというわけではないことに注意してください。)

回答者: Anonymous

Leave a Reply

Your email address will not be published. Required fields are marked *