2
5

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

【C#超入門】とても雑なintarface入門 ~これがモノホンのinterfaceだ~

Last updated at Posted at 2023-03-21

 僕の羊トークに対してよく頂く女性からの反応:「意味が分からない」

Dog.Walk() とか古めの記事の単純過ぎて逆に訳の分からないメソッドを並べられても理解出来ない人向け。interfaceの意味とかめんどいから全部カット。そんなんどうでもいいからとにかく使え。

自分も最近ようやくIDisPosableBaseを実装して理解出来た件。

こちらを先に見ましょう。

interfaceを介して実装したメソッドにアクセスする

参考リンク

interfaceの利点について(初学者向け)
interfaceの解釈や用い方が参考になった。

実用的なインターフェースであるIDisposableで試してみる

 イマイチ分かりにくいIDisposableの実装方法をまとめる。

要点 

IDisposable intarfaceを作成してクラスに継承させるとIDisposableインターフェース内のメソッドが使える! どんなクラスでもDispose可能になる!エクセレンッ!チョベリグッ!
 制約として継承先のクラスで全部intarfaceで指定してるメソッド名を使って実装書かないといけないよ! どこの念能力だよオイ めんどくさーい!

 この解決策として、基底クラス(Baseとなるクラス)にIDisposableを継承させて、そのクラスを目的とするクラスに継承させるという方法がよく取られています。どうみてもこの方が楽。

IDisposableBase クラス
   internal class IDisposableBase : IDisposable
    {

    }

これを目的のクラスに継承させます。

Alternate_FileExsists
 internal class Alternate_FileExsists: IDisposableBase
   {

   }

結果
(継承させないと using ステートメントで使用される型は、暗黙的に 'System.IDisposable' への変換が可能でなければなりません。 が出る)
image.png
またはAlternate_FileExsists.Dispose()

Alternate_FileExsistsにメソッドの実装追加を強制されることなくDispose
やUsingで囲むことが可能になりました。
つまり基底クラス作って間に噛ませるのが業界の標準的な使い方って訳。

実際に何か実装してみて試す

参考リンク(実はあんまり参考にならない)

もちろんGUIで行きますね。訳分からんし。
多分この説明の方が理解しやすいと思う。

外観はButtonが一個だけ、クリックイベントのみです。
image.png

まず、「対象の女性(キャラ)をウシ科動物的な何かに変える牧歌的なクラス」を作成。これにIFemaletoCowAnimalを継承させます。

対象の女性(キャラ)をウシ科動物的な何かに変える牧歌的なクラス.cs
using System.Windows;
namespace InterFace_WPFForm_Sample
{
    public interface IFemaletoCowAnimal
    {
       public  void ウシ科動物変換メソッド_不可逆的(string TargetFem);


    }

    public class 対象の女性キャラをウシ科動物的な何かに変える牧歌的なクラス : IFemaletoCowAnimal
    {

        public void ウシ科動物変換メソッド_不可逆的(string TargetFem)
        {
            string resultCowAnimal = TargetFem.Replace(TargetFem, "あなたはもうウシ科動物です");
            MessageBox.Show(resultCowAnimal);

        }

    }
}

次に、実行クラス.csを作成し、対象の女性キャラをウシ科動物的な何かに変える牧歌的なクラスを継承させます。

実行クラス.cs
 internal class 実行クラス:対象の女性キャラをウシ科動物的な何かに変える牧歌的なクラス
{
  ///ここでoverrideしてもいい全く別の実装でもいい
}
 

これはMainFormが複数の基底クラスを持つことが出来ないって仕様の為です。

呼び出し

private void ss1_Click(object sender, RoutedEventArgs e)
        {
         var exec = new 実行クラス();            
         exec.ウシ科動物変換メソッド_不可逆的("真木よう子");
            
        }

実行結果

これはGIF画像です
bandicam 2023-03-21 22-58-04-783.mp4_harua.gif

解説:~モノホンのintarfaceとは~

Interfaceを実装したクラスを他の様々なクラスに継承させる事で、汎用的な接続方法を提供する。これがIDisposableBase(インターフェースを実装した基底クラス)でやっている事なんですね(実際、どんなクラスも継承させれば破棄出来るようになる)。

interfaceを実装したクラスそのものにアクセスするのは実用的どころかあまり意味がないという事になります(対象メソッドが属するクラスのコンストラクタはどうなってるんだろうとふと思った。オーバーロードしたときとか。実は迂回してて呼んでないとか??)。

詰まるところメソッドの実装を強制とかは説明の半分に過ぎません。

で、冒頭に上げた中途半端な初心者向け説明が初心者を更に惑わすっていう悪循環に嵌るって寸法なんですよ。恐らく、これやる以外にinterfaceを付ける実用上の意味はあまりないです。それこそどういうメソッドを仕様として実装させてるのか明示させる目的ぐらい?

そのうちストラテジーパターンを試したい。

通常の継承や抽象クラスと何が違うのか?

 これまでの実装は普通にただの継承で出来る。
 つまり通常の継承とは基本的な違いはない。interfaceのメンバーは強制的にPublicになるが、実のところそれぐらいである。
 interfaceとはあるクラスについての仕様書・設計図、あるいは指示書(のようなもの)である。抽象クラスと違って、そのinterfaceを継承させるだけでそのクラスに(強制的に)実装の明確な方向性を与える事が出来る
 また、一つのクラスに複数のintarfaceを継承させたり、他のinterfaceを継承させたりと非常に自由度が高い(Class:にカンマ区切りで複数追加することが可能)

 抽象クラスはこの点においてinterfaceほどの自由度はない(かなり制約が多い)。
参考1  https://atmarkit.itmedia.co.jp/ait/articles/1801/10/news017.html

既定の実装を提供するには、仮想メンバを使う。
 既定の実装を提供しない場合で、汎用的な「インタフェース(外部との接続部分)」であるならばインタフェースを用いる。そうではなくて、特定のクラスの継承先だけに限定される性質のものならば、抽象クラスを使う。

参考2 C# インターフェースのサンプル(interface)

 あとはこれまで行ったように、基底クラスを作成し、実行クラスでoverrideして継承先クラス固有の振る舞いを与えるというのが基本的な使い方になる。IDisposableインターフェースの実装はこれを端的に表しているのである

注1 overridevirtual(仮想メンバ)自体はinterfaceじゃなくても出来る点に注目)。

注2 破棄するオブジェクトはシステム的にIDisposable型に変換可能でなければならないという制約があるので、IDisposable Interfaceは外せない

 元々の継承という機能だけでも十分強力な事がここからも見て取れる訳なんだけど、そこに規定の振る舞いを定義付け出来るという点でinterfaceというモノが与えられるのである。Visual Studioのコード補完機能と組み合わせると、より強力でスピーディにそのinterFaceに固有の振る舞いが与えられる。
 

interfaceの真の効力

 それはアプリの設計段階にこそある(実装してからでもいいけど)。
それなりに腕のいい人はクラス仕様の図柄もphotshop等で作った洒落たモノを用意しますが、それをCode上でメソッド定義として明示するのがinterfaceの主目的です。これは抽象クラスの成り立ちから考えても明らかでしょう。

クラス図の例:https://blog.goo.ne.jp/xmldtp/e/55891d24cb9a08f1dbfec110471af2df
image.png
これらをinterfaceとしてCode上で明示出来る。継承はあくまでその手段として用いている。つまりこのクラスはこういう仕様ですよっていう訳。

 アプリのクラス設計をとても丁寧にする人は、抽象クラスやinterfaceを作成してから実装を始めるのではないでしょうか。

 NeeView(https://bitbucket.org/neelabo/neeview/wiki/Home )内のCodeはインターフェースが多用されています(多分ストラテジーパターン -参考リンク )。

 このように、複雑な動作の大規模プロジェクト(個人製作だけど)ではプロジェクト製作者の脳内を整理するためにinterfaceは役立てられています。

 何かアプリ制作で煮詰まって整理したくなったらinterfaceを使って整理してみよう!的なモノなんです。だからinterfaceでは多重継承とか出来ちゃったりしてそういう制約がないんですねっていう。
 また、interface内で定義したメンバ変数はstaticなので即席のデータクラスとしても使えます。

 Microsoftの提示
 チュートリアル: C# 11 の機能を調べる - インターフェイスの静的仮想メンバー

interfaceを介して実装したメソッドにアクセスする

 
 ググって出るサンプルコードがへぼ過ぎてこれまで全く気付かないでいたのですが、Interfaceを介して継承先クラスにインスタンスを渡す事でメソッドを呼べます。

ググって出るのが大体コレ系なんだから理解出来る訳ないですね...
Microsoft learnとかたまに読むけどあんまり分かりやすくないんですよねアレ。

間違った従来の呼び出し方(クラスに直接アクセス)
    class View: IMessage
    {
 
        public void Morning()
        {
            Console.WriteLine("おはよう");
        }
 
        public void Hello()
        {
            Console.WriteLine("こんにちは");
        }
 
    }

    //メインクラス
    class Sample
    {
        static void Main(string[] args)
        {
          ///interfaceの効力を活かせない呼び出し
            View vw = new View();
 
            vw.Morning();
            vw.Hello();
            Console.ReadKey();
        }
    }

上記のコードのちゃんとした呼び出し方は正しくはこうなります
継承先のクラスでoverrideかoverloadすればポリモーフィズムやれます。

IMessage vw = new View();
            vw.Morning();
            vw.Hello();

Qiitaの参考になった記事

interfaceを介したメソッドを派生先のクラスからのみ呼び出せるようにする

protectedをメンバーに付ければいい。
interface経由の呼び出しは制限される。

コメント来てたのを読んでたらどうも制限出来るようなので試してみる。

    interface IOrigunDispose
    {
//protectedにする。Privateには出来ない。
        protected void Dispose();        
    }
    internal class TestClass1: IOrigunDispose
    {      
       public void Dispose()
        {
            MessageBox.Show("Call");
        }
    }
}
呼び出し
private void Button_Click(object sender, RoutedEventArgs e)
            {                    

              //不可
               IOrigunDispose test = new TestClass1();
               test.Dispose();
              //可能 
                TestClass1 test = new TestClass1();
                test.Dispose();
            }

結果
image.png

IOrigunDispose.Dispese()はアクセスできない保護レベルになっています。 が表示される

派生先のクラスから呼ぶ場合に制限はない。
つまり、この逆がやりたい場合(継承)はinterfaceを外してメソッドにprotectedを付けるしかない。

この場合はアクセス不可。

Non-interface
internal class TestClass1
    {      
       protected void Dispose()
        {
            MessageBox.Show("Call");
        }
    }

image.png

無理やりinterface型引数で制限するみたいなことをやってみたけどイマイチだったというオチが付いた。

参考
インターフェイスのデフォルト実装

2
5
2

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
5

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?