C#を勉強していたらインターフェースが出てきたけど
言ってる事が難しくてよく分からない…
一通り学習したけど
どんな時に使えばいいのか分からない
こんにちは!文系出身ながらも自力で勉強・実務をこなし、8年目に突入した現役プログラマの佐藤です。
みなさんは、インターフェースを理解できていますか?正直、筆者も勉強したての頃は「よく分からないな」と思っていました。説明を読んでも専門的な用語が多くなっていて、やる気がなくなってしまったりしていませんか?
難しく書いてあると、いったいどんなことが出来て、いつ使うものなのかがイメージしづらいですよね。そんなあなたのために、この記事ではインターフェースの仕組みからなるべくわかりやすい言葉で解説していきます。
そして、最後には一緒に実践してみることで、インターフェース自体に慣れ親しんでいただき、苦手意識を払しょくします!
基礎は分かったから実装の仕方が知りたい!
もちろん、そんな方のためにすぐ出来る実装の方法をばっちり解説していきます。C#を学習しているなら、インターフェースの知識は必須です。インターフェースを使いこなすことが出来るように、一緒に学習していきましょう。それでは早速、仕組みから学習していきます!
インターフェースについて学ぼう
この章では、インターフェースの基礎や、メリットについて紹介します。段階を踏んで行けば必ず理解できるので、順番に見ていきましょう。
インターフェース?実装?まずは言葉の意味から!
「インターフェース」の言葉自体の意味は難しめの言葉で言うと「接触面」とか「境界面」という意味です。とても簡単に言うと「何かと何かの間を繋ぐ部分」=「インターフェース」ということです!
アプリを使うときの操作画面は、アプリと人を繋ぐ部分なので「ユーザーインターフェース」なんて呼ばれたりします。まずは、何かと何かの橋渡しとかしてくれる仲介役なんだな~と理解しておきましょう。
そしてインターフェースを勉強していると、「実装」という言葉を目にしますよね。とても重要な言葉なので、ここで一緒に解説します。「実装」には新たな機能・部品を組み込むという意味があります。ここでは「実装」=「機能とかを新しく追加する」と覚えてしまいましょう!
「クラスにインターフェースを実装する」と書いてあれば「クラスにインターフェースの機能を新しく追加するよ」という風に言い換えることが出来ます。言い換えてしまえば、思ったより簡単ですよね。言葉の意味はつかめたでしょうか?
これを踏まえて、C#のインターフェースでは何かできるか見ていきましょう。
インターフェースを使えばメソッドの実装を強制できる
インターフェースと実装についてはなんとなくわかったけど
メソッドの実装を強制できるって何?まだ意味がわからない…
そんな風に思ったあなた、安心してください。これから丁寧に説明していきます! インターフェースという言葉は「何かと何かの間を繋ぐ部分」と説明しました。C#でのインターフェースは「クラスを作った人」と、「そのクラスを使う人」の間で、使い方の約束事を決めることができる仕組みの事を言います。
使い方の約束事の内容は、「このインターフェースを使うときはこのメソッドの中身を必ず作ってね!」とか、「そのメソッドには、必ずこんな引数を渡してね!」などです。言葉だけでは想像しづらいとおもうので、実際に簡単なインターフェースと、その使い方を見てみましょう。
using System; namespace Sample { //インターフェース interface IMessage { void Hello(); } //インターフェースを実装するクラス class View : IMessage { public void Morning() { Console.WriteLine("おはよう"); } public void Hello() { Console.WriteLine("こんにちは"); } } //メインクラス class Sample { static void Main(string[] args) { View vw = new View(); vw.Morning(); vw.Hello(); Console.ReadKey(); } } }
IMessageインターフェースを、Viewクラスに「実装」してみました。ここまでだったら普通の継承とあんまり変わりないですよね。試しに、Viewクラスの次の部分をこんな感じでコメントアウトしてみてください。
class View : IMessage { public void Morning() { Console.WriteLine("おはよう"); } //public void Hello() //{ // Console.WriteLine("こんにちは"); //} }
コメントアウトしたところで、こんなエラーが表示されていませんか?
これは、IMessageインターフェースで決めてた「Hello」っていうメソッドがちゃんと実装されてないよ!というエラーです。実装していないとエラーがでるため、メソッドの実装を強制できるということですね。
このように、使う人それぞれがバラバラな使い方をしてしまうのを防ぐことが出来ちゃう便利な仕組みがインターフェースなんです。インターフェースのことがちょっとずつ理解出来てきましたか? そしてここまで学習している方で、気付いた方もいると思います。
これって、すごく抽象クラスと似ていますよね? もちろん、似ていますが違うものです! 間違えて覚えてしまわないよう、次の項目で抽象クラスとインターフェースの違いについて詳しく解説していきます。
抽象クラスと似ているけど違うもの
継承を学習した時に出てきた「抽象クラス」を覚えていますか? 「抽象クラス」は「抽象メソッド」を1つ以上含むクラスです。そして、「抽象メソッド」は継承先でのオーバーライドを強制する仕組みでした。
メソッドの実装を強制するという点では、インターフェースも抽象クラスも変わりません。これって…インターフェースと同じじゃない…? と思いますよね。ですが、「抽象クラス」と「インターフェース」は全く違うものなので注意しましょう!
抽象クラスでは抽象メソッド以外に、普通のメソッドも書いても問題ありませんでした。「抽象メソッド」が一つでも含まれていれば、「抽象クラス」になれるためです。「インターフェース」ではメソッドの具体的な内容を一切書いてはいけません、クラス変数を持つのもNGになっています。
そして、外部からの仲介役をこなすために、public以外のメソッドを作ることができません。そして、抽象クラスは一つしか継承できませんでしたが、インターフェースなら何個でも実装して使うことが出来ます。
このように、違う部分のほうが多いのでごちゃごちゃにして覚えないように注意しましょう。もちろん、使われる場面も違ってきますので、状況によって適切な方を選べるようになれるのが一番良いです。ですが、初めの頃は「違うものなんだな」ということだけしっかり理解できていれば、全く問題ありません!
インターフェースの基礎的な部分の解説は以上です。インターフェースと実装の言葉の意味から、インターフェースがメソッドの実装を強制できる便利な仲介役というところまで理解できたでしょうか? 仕組みを何となく理解出来たら、次は実際に手を動かして慣れていきましょう!
まずは使ってみよう!
自分でインターフェースを作る前に、まずインターフェースを体験してみましょう。この章では、すでに用意されているインターフェースの使い方を紹介します。
すでに用意されているインターフェースがある
実は、.NET Frameworkには用意されているインターフェースがあるんです。用意されているのは便利な機能が簡単に使えるように仲介してくれるものばかりなので、ここで実際に使ってみましょう!
今回紹介するのは、その中でも使う場面が多いおすすめのインターフェースです。実際に使ってみて、インターフェースに慣れてみましょう。
リソースの開放忘れを防ぐ!IDisposableを使ってみよう
ここからは、インターフェースを使う練習としてサンプルコードを使いながら学習していきます。まずは「IDisposable」というインターフェースを使ってみましょう!
皆さんはStreamReaderクラスなどのusingステートメントを使ったことがありますか? usingステートメントを使えば、開いたStreamReaderを閉じなくても使い終われば勝手に閉じてくれてましたよね。
usingステートメントについて詳しく知りたくなった方は、この記事も併せて読んでみてください!usingステートメントの便利な使い方を解説しています。
実はこのusingステートメントを使えるようにするインターフェースがすでに用意してあるんです。そのインターフェースは「IDisposable」といい、自作のクラスにも実装することが出来ます!
実際にどのように実装するのか見てみましょう。今回は「作業用のテキストファイルを作り、最後は必ずそのテキストファイルを削除するクラス」を作ってみます!
using System; using System.IO; namespace Sample { //IDisposableを実装してみる自作のクラス class TextFile :IDisposable { private string filepath; public TextFile(string path) { //テキストファイルを作る filepath = path; using(StreamWriter sw = new StreamWriter(filepath)){} } public void Dispose() { //作ったファイルを削除する File.Delete(filepath); } } class Sample { static void Main(string[] args) { //TextFileクラスを使う using (TextFile tf = new TextFile(@"C:satotest.txt")) { Console.WriteLine("作業用ファイルの有無:" + File.Exists(@"C:satotest.txt")); } Console.WriteLine("作業用ファイルの有無:" + File.Exists(@"C:satotest.txt")); Console.ReadKey(); } } }
実行結果:
作業用ファイルの有無:True 作業用ファイルの有無:False
作ったファイルの有る無しが分かりやすいように途中でコンソールに書き出しました。usingの中ではファイルがありましたが、外に出るともう無くなっていますよね? TextFileクラスの中を順番に見ていきましょう。まずは、次のようにIDisposableインターフェースを使う宣言をします。
class TextFile:IDisposable
これをすると「Disposeメソッドを実装しないとエラーだよ」とエラーを出してきます。このDisposeメソッドがとても大切なポイントです! 実は、usingステートメントが終わる時、このDisposeメソッドが必ず自動的に呼び出されるという流れになっているのです。
今回は、作った後は必ず削除したかったのでDisposeメソッドにはファイル削除の命令を書きます。あとはメインのクラスで自分の作ったクラスを使ってみましょう。usingステートメントが終わると、先ほど作ったDisposeメソッドが自動的に呼び出されファイルが削除されます。
ファイルが自動的に削除された理由が分かったでしょうか? このインターフェースを使えば、最後に絶対してほしいことを忘れずに済むので非常に便利です。みなさんも、自分で作ったクラスで最後に絶対してほしいことがある時に是非実装してみてくださいね!
比較が出来るIComparableを使ってみよう
次は「IComparable」というインターフェースを使って、練習していきましょう。みなさんは値を比較する時に「CompareTo」を使ったことはありますか? 比べた結果で「-1」とか「1」とかが返ってくるメソッドです。
CompareToメソッドについて知りたくなった方は、次の記事で学習・復習してみてください。日付や文字列の比較について紹介しています。
実は、そのCompareToメソッドを自作のクラスに実装できるインターフェースがすでに用意されているんです。自分のルールで比較して、並べ替えたい時に非常に便利です。早速、使ってみましょう!
ここでは次のルールでタスクを並べ替えるクラスを作ってみます
- 先頭の数字が「2」の時:最優先
- 先頭の数字が「1」の時:普通
- 先頭の数字が「0」の時:後回し
using System; using System.Collections.Generic; namespace Sample { //IComparableを実装してみる自作のクラス class TaskCompare :IComparable<TaskCompare> { //タスク名 public string taskName { get; } //タスクの順位 public int taskNum { get; } //コンストラクタ public TaskCompare(string taskName) { this.taskName = taskName; //タスク名から数字だけ切りとる taskNum = int.Parse(taskName.Substring(1, 1)); } //比較するメソッド public int CompareTo(TaskCompare task) { //自分よりも引数のほうが大きかったら if(taskNum < task.taskNum) { return 1; } //自分よりも引数のほうが小さかったら else if (taskNum > task.taskNum) { return -1; } //自分と同じだったら else { return 0; } } } class Sample { static void Main(string[] args) { List<TaskCompare> tasks = new List<TaskCompare>(); tasks.Add(new TaskCompare("【1】食器洗い")); tasks.Add(new TaskCompare("【2】洗濯")); tasks.Add(new TaskCompare("【0】買い物")); tasks.Add(new TaskCompare("【1】掃除機")); tasks.Add(new TaskCompare("【1】洗濯物干し")); //順位順に並べ替える tasks.Sort(); //表示する foreach (TaskCompare c in tasks) { Console.WriteLine($"タスク名: {c.taskName}"); } Console.ReadKey(); } } }
実行結果:
タスク名: 【2】洗濯 タスク名: 【1】食器洗い タスク名: 【1】掃除機 タスク名: 【1】洗濯物干し タスク名: 【0】買い物
リストに入れた時と順番が違っているのが分かるでしょうか? しっかり先頭の番号順に並べ替えていますよね。TaskCompareクラスに比較できるCompareToメソッドが実装されています。
IComparableインターフェースはこのCompareToメソッドの実装が出来るものです。今回は先頭の数値同士を比較して、自分より相手が大きいときは「1」を、小さければ「-1」、同じであれば「0」を返すように作りました。
そして、このクラスを格納するリストでソートすると、自動的にCompareToメソッドを使ってソートしてくれます! このように、自作クラスを作り、自己流のルールで並び替えたり比較したい時には非常に便利なインターフェースになっています。
最初に紹介したIDisposableよりは使う頻度が低いかもしれませんが、こちらも是非マスターしてみてください。ここまで使いこなせるようになれば、次はもう自分でインターフェースを作ることもできます!早速やり方を見ていきましょう。
自分でインターフェースを作ってみよう
実際に使ってみることで、少しインターフェースに慣れることができたでしょうか? 最後に自分でインターフェースを作ってみましょう。
インターフェースの作り方
インターフェースを作るにはいつも「class」と書いているところに「interface」と書くだけで作ることが出来ます。ここではサンプルとして、「挨拶」インターフェースを作ってみます。
//インターフェース interface IMessage { void Hello(DateTime time); void Morning(DateTime time); void Night(DateTime time); }
たったこれだけで、インターフェースの完成です。インターフェースの名前には先頭に「I」を付けるのがルールになっていますので、皆さんも作る時には先頭に「I」を付けて作りましょう。
そして、メソッドの中身は何も書いてはいけません。返り値の型や、引数は指定しても大丈夫です。それでは、作ったインターフェースを早速実装してみましょう。先程、お試しでインターフェースを実装してみたので、もう慣れていますよね。やり方は変わりませんので、安心してください!
作ったインターフェースを実装してみよう
作ったインターフェースを実装してみましょう! 実装するには、継承と同じように書いた後にメソッドを実装していきます。
using System; namespace Sample { //インターフェース interface IMessage { void Hello(); void Morning(); void Night(); } //インターフェースを実装するクラス class View : IMessage { public DateTime nowTime { get;} public View() { nowTime = DateTime.Now; } public void Hello() { Console.WriteLine("こんにちは"); Console.WriteLine("今の時間は" + nowTime + "です"); } public void Morning() { Console.WriteLine("おはよう"); Console.WriteLine("今の時間は" + nowTime + "です"); } public void Night() { Console.WriteLine("こんばんは"); Console.WriteLine("今の時間は" + nowTime + "です"); } } //メインクラス class Sample { static void Main(string[] args) { View vw = new View(); int time = int.Parse(vw.nowTime.ToString("hh")); if(time >= 6 && time < 12) { vw.Morning(); } else if(time >= 12 && time < 18) { vw.Hello(); } else { vw.Night(); } Console.ReadKey(); } } }
実行結果:
こんにちは 今の時間は2019/03/05 13:02:01です
先程お試しで使ったときのように、まずはインターフェースを使う宣言をします。
//インターフェースを実装するクラス class View : IMessage
そのあとは、インターフェースで決められているメソッド処理の中身を書いて実装していきます。この時のメソッドは必ずpublicでなければいけません、また名称、返り値の型も同じものを使わないければならない点に注意しましょう。
最後に、メインのクラスで時間帯によって呼び出すメソッドを分岐させて完成です。一度お試しでやったのもあって、すんなり行けたでしょうか? インターフェースで決められているメソッドを忘れず実装すればOkなので、そこまで複雑ではないですよね。
以上で、実装方法に関しての解説は終了です。自分でインターフェースを作るのはまだ先かもしれませんが、覚えておけばすでに用意されているインターフェースを使うときにも仕組みがイメージしやすくなります。この解説を読んで、ぜひ自分でインターフェースを作ってみてくださいね。
まとめ
インターフェースについて順番に勉強してきました! いきなり難しい言葉を読んで理解しようとするよりは、簡単な言葉で雰囲気をつかんでしまって実際に使ってみるのが一番いいと思います。
ここで紹介したインターフェースをぜひ活用して、自作クラスを充実させていってくださいね! そして、慣れたころには自分でインターフェースを作成して使って行きましょう。それでは、また次の解説で!