y-staba
@y-staba

Are you sure you want to delete the question?

Leaving a resolved question undeleted may help others!

C#におけるインターフェースの実装について

C#におけるインターフェースの実装について

インターフェースを実装する際、実装するメソッドにアクセス修飾子publicを付与しないとコンパイルエラーになります。

そこで、以下の2点について解決したいです
・publicを付与しなければならない理由
・実装するメソッドをprivateやprotectedにすることは可能か。可能であればどのような方法があるか。

発生している問題・エラー

 Error CS0737: 'ClassHoge' は、インターフェイス メンバー 'IHoge.Foo()' を実装していません。'ClassHoge.Foo()' は public ではないため、インターフェイス メンバーを実装できません。 (CS0737) 

該当するソースコード

interface IHoge
{
    void Foo();
}

internal class ClassHoge : IHoge
{
    //publicを指定しないとコンパイルエラーになる
    void Foo()
    {
        Console.WriteLine("インターフェースIHogeを実装したクラスだよ");
    }
}
0

4Answer

インターフェースの意義を理解出来ていないのかなと感じました。
HogeClassにIHogeを実装させる意味は、Fooを他のクラスから使えることを宣言することにあります。
privateだと他クラスからアクセス出来ないのでインターフェースを実装する意味がなくなり、「他クラスからFooを使えるのがIHogeである」というルールが適用出来ないためprivateには出来ません。

なぜprivateやprotectedにしたいのかの理由が示されると、より深い回答が出来るかもしれません

3Like

Comments

  1. @y-staba

    Questioner

    インターフェースの意義を理解出来ていないというご指摘は、おっしゃる通りで私も実感しております。実装クラス側でprivateを使用できないのは、メソッドを他のクラスから呼び出せなくなるということは理解できました。

    別の例になりますが、ClassHogeのFooを、「同じアセンブリ内の他のクラスからのみ利用できるようにしたい」などの限定があった場合、Fooにinternalを指定できた方が便利なのではないかと考えています。
    この状況においてはなぜそれができないのか、「インターフェースのその状況で使うべきではないなぜなら〜」「それを可能するとインターフェースのルールが適用できなくなる。〜」などご指摘していただけると助かります。

  2. IHogeをinternalには出来なかったですっけ?
    IHogeがpublicである以上、それを実装するメソッドもpublicでなければ理屈が通らなくなると思いました

  3. なぜHogeClassにIHogeを実装させたいのかは自分の言葉で説明できますか?

  4. @y-staba

    Questioner

    IHogeをinternalには出来なかったですっけ?

    IHogeのFooをinternalにすることは可能でした。ただ、その場合でもClassHogeのFooはpublicにしないとコンパイルエラーになります。

    なぜHogeClassにIHogeを実装させたいのかは自分の言葉で説明できますか?

    IHogeを実装することは、メソッドFooをHogeClassが確実に備え、他のクラスがFooを呼び出せることを実現するためだと考えています。

    そうした考えがあるから「Fooのアクセス修飾子にprivateを指定することは他のクラスからFooを呼び出せなくなるからダメ」ということは、回答者様のおかげで理解できました。
    (理解不足の箇所があればご指摘いただけると助かります。)

  5. なるほど、確かにinterfaceがinternalであれば実装メンバーもinternalで許されるのではというのはそうですね。
    型のアクセシビリティは型のアクセシビリティで、メンバーのアクセシビリティはメンバーのアクセシビリティというところでしょうか。

    どうしても「HogeClassのFooはinternalにしつつIHogeは実装したい」ということであれば、直接的なやり方ではないですがインターフェースを明示的に実装すれば結果として実現はできます。同一アセンブリ内でもHogeClass型としては呼び出せないですが(別途オーバーロードでinternalメソッドを定義して実装を共有すればいいかも?)

    私の持つ知識ではこの辺りの回答が限界ですが、もしかしたらinternalなinterfaceのメンバーはinternalアクセシビリティで実装出来るようにしたい、みたいなC#の言語仕様提案もあるかもしれないですね

  6. 私がインターフェースのデフォルト実装が入って以降の
    インターフェース周りをあまり触ってなく、回答もスマホからなのでちょっと曖昧なのですが、インターフェース側でメンバーにアクセシビリティとしてinternalを指定すると比較的希望に近い動作になるかもしれません。

  7. @y-staba

    Questioner

    ご回答ありがとうございます。
    求めていたものにとても近い意見です。

publicを付与しなければならない理由

デフォルトのアクセス修飾子の違いによります。質問のコードではアクセス修飾子が明示的に設定されてないので、デフォルトでインターフェイス IHoge の Foo は public に、クラス ClassHoge の Foo は private になります。

実装するメソッドをprivateやprotectedにすることは可能か。可能であればどのような方法があるか。

C# 8.0 以降 (.NET Core 3 以降) はインターフェイスで private や protected に設定することが許されているので可能です。

2Like

Comments

  1. @y-staba

    Questioner

    ご回答ありがとうございます。

    インターフェース側(IHogeのFoo)のアクセス修飾子に関わらず、実装クラス側(ClassHogeのFoo)では必ずpublicを付与しないと、インターフェースを正しく実装できないということでしょうか。

  2. 「インターフェースを正しく実装」の意味が分かりませんが・・・

    インターフェース側(IHogeのFoo)のアクセス修飾子に関わらず、

    C# 8.0 より前 (.NET Core 3 より前、.NET Framework は全て) までは IHoge の Foo に protected とか private は付けられない(付けたらエラー)です。その場合、「インターフェース側(IHogeのFoo)のアクセス修飾子に関わらず」なんて話はなくて、インターフェース側は public 以外ないのは分かりますか?

    なので、

    実装クラス側(ClassHogeのFoo)では必ずpublicを付与しないと、インターフェースを正しく実装できないということでしょうか。

    「正しく実装できない」というより、実装クラス側は public 以外にどうしようもないのは分かりますか?

    C# 8.0 以降 (.NET Core 3 以降) に private や protected に設定することが可能になったのは、下の記事に書いてあるように、インターフェイスが関数メンバーの実装を持てるようにするというのが目的だそうです。アクセス修飾子に private や protected が使えるようになったのは、その目的を実現するためのついでということで、そういう目的がなければ考えなくていいことだと思います。

  3. @y-staba

    Questioner

    C# 8.0 より前 (.NET Core 3 より前、.NET Framework は全て) までは IHoge の Foo に protected とか private は付けられない(付けたらエラー)です。その場合、「インターフェース側(IHogeのFoo)のアクセス修飾子に関わらず」なんて話はなくて、インターフェース側は public 以外ないのは分かりますか?

    これについてはわかります。

    「正しく実装できない」というより、実装クラス側は public 以外にどうしようもないのは分かりますか?

    こちらについては理解できておりません。実装クラスでアクセス修飾子を省略する場合、メソッドがprivateになることは、回答者様の解説で理解できております。ただ、「インターフェースでpublic、実装クラスでprivate」だと、何が問題なのか理解できておりません。

  4. 「インターフェースでpublic、実装クラスでprivate」だと、何が問題なのか理解できておりません。

    やってみてからそういうことを言ってますか? あなたの環境ではエラーにならないのですか? 自分の環境ではエラーですけど。

    interface.png

    上があなたの質問の最初の状況「Error CS0737: 'ClassHoge' は、インターフェイス メンバー 'IHoge.Foo()' を実装していません。'ClassHoge.Foo()' は public ではないため、インターフェイス メンバーを実装できません。 (CS0737) 」と同じなのは分かってますか?

  5. @y-staba

    Questioner

    エラーになることは試してみたので分かっており、エラーになる原因が「インターフェースでpublic、実装クラスでprivate」だということも回答者様からのご指摘で理解しております。

    ただ、「インターフェースでpublic、実装クラスでpublic以外のアクセス修飾子」を禁止にする理由がわからない状況です。禁止にするということは、それが許されると何かしら不都合があるから禁止にしているのではないかと考えています。

    仮に、「インターフェースでpublic、実装クラスでpublic以外のアクセス修飾子」が可能な場合、どのようなデメリットがあるのでしょうか。

  6. 「インターフェースでpublic、実装クラスでpublic以外のアクセス修飾子」を禁止にする理由がわからない状況です。

    あなたが分かる・分からないの問題ではないのです。そういう仕様だからコンパイラも通らないのです。そう言っても分からない?

    【追記】

    何故あなたが interface 側で public なのに、実装側で private とか protected にする必要があるのか、その理由を万人が理解できるように十分な根拠を示して説明することはできますか。

  7. なぜそういう仕様なのかを知りたいというお話ではないでしょうか?

  8. なぜそういう仕様なのかを知りたい

    以下のように考えると分かりやすいかもしれません。

    C# の Interface の仕様、

    18.1 General
    https://learn.microsoft.com/ja-jp/dotnet/csharp/language-reference/language-specification/interfaces#181-general

    に "An interface defines a contract. A class or struct that implements an interface shall adhere to its contract." と書いてあるように、クラスが interface を継承するということはその interface に定義されているメソッド等が実装されているという契約です。

    interface に定義されている public メソッドを、クラス側で private とか protected メソッドとして実装できたら、それは契約違反ということになります。

    別の言い方をすると、外部にメソッド等をパブリックに公開する目的なのに、private とか protected で隠ぺいするのは全く理にかなってないとも言えます。

  9. @y-staba

    Questioner

    ご回答ありがとうございます。

    別の言い方をすると、外部にメソッド等をパブリックに公開する目的なのに、private とか protected で隠ぺいするのは全く理にかなってないとも言えます。

    インターフェースの目的を正しく理解できていなかったため、勉強になります。
    例えば、internalなFooをClassHogeに確実に備えさせたい場合、そもそもインターフェースを使用すべきではない(使用できない)ということでしょうか。この場合、どうような方法で実現可能か教えていただきたいです。

  10. 例えば、internalなFooをClassHogeに確実に備えさせたい場合、そもそもインターフェースを使用すべきではない(使用できない)ということでしょうか。

    その通りです。実際にやってみてそのことは身をもって分かったはずなのに、なんでまたそういうことを聞いてくるのですか?

    この場合、どうような方法で実現可能か教えていただきたいです。

    interface にこだわる理由が分かりませんが、できないことは当たり前ですが何ともならないので、interface を使うのは諦めて、abstract を使うことを考えてはいかがですか?

  11. @y-staba

    Questioner

    その通りです。実際にやってみてそのことは身をもって分かったはずなのに、なんでまたそういうことを聞いてくるのですか?

    確認のために聞いただけで深い意味はございません。

    interface にこだわる理由が分かりませんが、できないことは当たり前ですが何ともならないので、interface を使うのは諦めて、abstract を使うことを考えてはいかがですか?

    ありがとうございます。abstractを用いた手法を調べてみたいと思います。

下記ページによりますと、

一方で、C#でクラス(及び構造体の)メンバーにアクセス修飾子を付けない場合は、privateになります。C#の場合、privateを付けても省略しても同じなので、個人的には省略した方がいいと思います。

とある為、明示的な意味合いがあるように見受けられます。
(明示的なインターフェイスというと、実装するクラスにpublicを付けずにvoid IHoge.Foo() {...}とも書けますね)

また、下記ページによるとC#8.0からインターフェイスの書き方が変わっている様です。

アクセシビリティ

C# 7.3 までは、インターフェイスのメンバーは常に public で virtual でした。 C# 8.0 からは、明示的に指定することでクラスと同じく、protected などのアクセシビリティを指定できます。

ちなみに、省略時の挙動は今まで通り public virtual です。 クラスの場合の省略時は private なので、クラスとは挙動が異なります。

1Like

Comments

  1. 同じページですがprivateやprotectedを使用したインターフェイスのサンプルコード(を弄っています)。

    using System;
    
    interface I
    {
        // 未指定の挙動は今まで通り、public virtual。
        void Public()
        {
            Console.WriteLine("I.Public");
            Private();
        }
     
        // 明示することでそれ以外のアクセシビリティを指定できるように。
        // protected なら派生クラス・派生インターフェイスから、
        // private なら自分自身からのみ呼び出し可能。
        protected void Protected()
        {
            Console.WriteLine("I.Protected");
        }
    
        private void Private()
        {
            Console.WriteLine("I.Private");
        }
    }
     
    interface IDerived : I
    {
        void M()
        {
    	    Console.WriteLine("IDerived.M");
            Public();
            Protected();
            // Private(); はダメ
        }
    }
    
    class C : I, IDerived
    {
        static void Main()
        {
            var c = new C();
            ((I)c).Public();
    	    ((IDerived)c).M();
        }
    }
    

    全然求めているものと違うとおもいますが、内部にprivateなインターフェイスを持たせたり?

    サンプルを動くように弄っています。

    using System;
    
    public class Sample
    {
    
        static void Main()
        {
            var t = new Test();
            t.Test1();
        }
    
        private interface ITest1
        {
            void Test1();
        }
    
        private class Test : ITest1
        {
            public void Test1()
            {
                Console.WriteLine("Test1");
            }
        }
    }
    

  2. @y-staba

    Questioner

    ご回答いただきありがとうございます。

    現在あまり理解できていませんが、「privateやprotectedを使用したインターフェイス」について紹介していただいたサンプルコードと記事を参考にして学びたいと思います。

  3. 何かのお役に立てたら幸いです。

皆様、ご回答していただき本当にありがとうございます。

そもそも私がインターフェースの意義を理解できていないので、
まずはそこを学び直したいと思います。

0Like

Comments

  1. その前に、回答をもらってフィードバックを返してないのだから、それを先にしませんか? 役に立った/立たなかったぐらいはすぐにでも返せるのでは? 役に立たなかったなら、どこがどう期待する答えと違うかも書いてもらえませんか?

  2. @y-staba

    Questioner

    返信を怠ってしまい、申し訳ございません。
    いただいた回答にフィードバックをお返しします。

Your answer might help someone💌