内部クラスでパブリックメソッドを使用する理由


250

私たちのプロジェクトの1つには、次のようなコードがたくさんあります。

internal static class Extensions
{
    public static string AddFoo(this string s)
    {
        if (s == null)
        {
            return "Foo";
        }

        return $({s}Foo);
    }
}

「型を後で公開する方が簡単です」以外に、これを行う明確な理由はありますか?

私はそれが非常に奇妙なエッジケース(Silverlightでの反映)でのみ重要であるか、まったく重要でないと思います。


1
私の経験では、それが通常の習慣です。
phoog

2
@ phoog、OK。しかし、なぜそれが通常の習慣なのでしょうか?たぶん、一連のメソッドを内部に変更する方が簡単でしょうか?クイックフィックス->代わりにタイプを内部にマークしますか?
bopapa_1979

それは私がいつも想定していたことです。しかし、十分に検討された分析はありません。そのため、回答を投稿しませんでした。
phoog

2
エリックリッペルトの答えは私の考えを非常によくまとめており、また、抜け道を見つけるために私の脳に潜んでいた何かを持ち出します。インターフェイスメンバーの暗黙的な実装はパブリックでなければなりません。
phoog

その方法は等しいではないreturn s + "Foo";ですか?+オペレータは、nullまたは空の文字列を気にしません。
Mikkel R. Lund 2016年

回答:


418

更新:この質問は、2014年9月に私のブログの主題でした。すばらしい質問をありがとう!

コンパイラチーム内でも、この問題についてはかなりの議論があります。

まず、ルールを理解するのが賢明です。クラスまたは構造体のパブリックメンバーは、包含型にアクセスできるすべてのユーザーがアクセスできるメンバーです。したがって、内部クラスのパブリックメンバーは事実上内部です。

では、内部クラスが与えられたら、アセンブリでアクセスしたいそのメンバーをパブリックまたは内部としてマークする必要がありますか?

私の意見は、そのようなメンバーをパブリックとしてマークすることです。

「このメンバーは実装の詳細ではない」という意味で「パブリック」を使用しています。保護されたメンバーは実装の詳細です。派生クラスを機能させるために必要なことは何かあります。内部メンバーは実装の詳細です。このアセンブリの内部で何かが正しく機能するためには、メンバーが必要です。パブリックメンバーは、「このメンバーは、このオブジェクトによって提供される主要な文書化された機能を表します」と述べています。

基本的に、私の態度は次のとおりです。この内部クラスをパブリッククラスにすることにしたとします。そのためには、クラスのアクセシビリティという1つだけを変更したいと思います。内部クラスをパブリッククラスにすると、内部メンバーもパブリックメンバーにする必要がある場合、そのメンバーはクラスのパブリックサーフェス領域の一部であり、そもそもパブリックである必要があります。

他の人は同意しません。メンバーの宣言をちらりと見て、内部コードからのみ呼び出されるかどうかをすぐに知りたいという条件があります。

残念ながら、これは常にうまくいくとは限りません。たとえば、内部インターフェイスを実装する内部クラスは、クラスのパブリックサーフェスの一部であるため、実装メンバーをパブリックとしてマークする必要があります。


11
私はこの答えが好きです...それは、可能な限り自己文書化するコードを書くことに対する私の態度によく適合します。特に、他のチームメンバーがあなたの慣習を理解している場合、スコープはブロードキャストの意図を伝える優れた方法です。
bopapa_1979

10
自分の言いたいことを言いますが、自分よりもはるかにうまく定式化します(インターフェイスの角度を上げるためにもですが、暗黙のメンバー実装にのみ当てはまります)。
phoog

2
インターフェイス部分は重要です-内部を使用してインターフェイスメソッドを実装することは不可能であり、パブリックまたは明示的なインターフェイス宣言です。つまり、publicという単語には2つの意味があります。
Michael Stum

8
アイデアの観点からは、あなたの支持する主張publicは非常に説得力があります。しかし、「常にうまくいくとは限らない...」というのは特に強力だと思います。一貫性が失われると、「一目でわかる」メリットが損なわれます。internalこのように使用することは、時には間違っている直感を故意に構築することを意味します。ほぼ正しい直感を持つことは、IMOがプログラミングにとって恐ろしいことです。
ブライアン

3
ブログ投稿の重要な引用:「私のアドバイスは、チーム内で問題について話し合い、決断を下し、それを守ることです。」
bopapa_1979 2017年

17

クラスである場合internal、あなたがメソッドをマークするかどうか、アクセシビリティの観点から重要ではありませんinternalpublic。ただし、クラスがそうであった場合に使用するタイプを使用することは依然として良いですpublic

これにより、からinternalへの移行が容易になると言う人もいpublicます。また、メソッドの説明の一部としても機能します。Internalメソッドは通常、自由なアクセスに対して安全ではないpublicと見なされていますが、メソッドは(ほとんど)無料のゲームと見なされています。

クラスで、internalまたはクラスpublicと同じように使用することで、public予想されるアクセスのスタイルを確実に伝えpublic、将来クラスを作成するために必要な作業を容易にします。


私は良いAPIを作成し、私の意図する消費者が私が意図することをできるようにしながら、すべてを可能な限り最大限に制限する傾向があります。また、クラスが公開されている場合と同じように、メンバーにマークを付けます。より具体的には、メンバーが常に安全であると判断した場合にのみ、メンバーに公開マークを付けます。そうしないと、後で誰かがクラスをパブリックにマークする(それが起こる)と、安全でないメンバーが公開される可能性があります。
bopapa_1979

@Eric:準備ができるまでメソッドの安全性を判断するのを忘れたい場合は問題ありませんが、私は安全であると見なされたメソッドのみを参照していました。その場合、公開はありません。
Guvante

10

私は多くの場合、内部ではなく内部クラスでメソッドをパブリックとしてマークします。a)それは本当に問題ではありません。b)internalを使用して、メソッドが意図的に内部であることを示します(これを公開したくない理由がいくつかあります)パブリッククラスのメソッドです。したがって、内部メソッドがある場合、それをパブリックに変更する前に内部である理由を本当に理解する必要があります。一方、内部クラスでパブリックメソッドを扱う場合は、その理由を本当に考えなければなりません。各メソッドが内部である理由とは対照的に、クラスは内部である。


答えてくれてありがとう。私は頑強にコーディングするときに「すべて」が重要であると主張する傾向があります:)
bopapa_1979

1
あなたは正しいエリックです、それはちょっと捨てられたコメントでした。私の答えの残りがある程度役に立ったことを願っています。エリックリッペルトの答えは私が表現しようとしていたことだと思いますが、彼はそれをとてもよく説明しました。
ƉiamondǤeezeƦ

8

「後で型を公開する方が簡単なのでは?」それは...ですか。

スコープ規則は方法が唯一のように表示されますことを意味しinternal、本当に方法がマークされているかどうかは関係ありませんので、 - publicまたはinternal

頭に浮かぶ可能性の1つは、クラスパブリックで、後で変更されinternal、開発者がすべてのメソッドのアクセシビリティ修飾子を変更する必要がなかったことです。


1
+1は、「パブリッククラスが内部になった」というシナリオを明らかにするためのものです。
bopapa_1979

8

場合によっては、内部型がパブリックインターフェースを実装していることもあります。これは、そのインターフェースで定義されたメソッドをパブリックとして宣言する必要があることを意味します。



0

internalメンバーは同じアセンブリ内からのみアクセスできると述べています。そのアセンブリ内の他のクラスはinternal publicメンバーにアクセスできますが、privateまたはprotectedメンバーにアクセスできないか、internalまたはアクセスできません。


しかし、メソッドがのinternal代わりにマークされている場合public、それらの可視性は変わりません。それがOPが求めていることだと思います。
2012

1
@zapthedingbat-投稿は事実として正しいですが、実際に質問に答えていますか?
2012

1
問題は、なぜそのようにマークするのかということです。スコープの基本を理解しています。しかし、試みをありがとう!
bopapa_1979

0

今日私は実際これに苦労しました。これまではinternal、クラスがそうである場合はすべてのメソッドにマークを付ける必要がinternalあり、特にエンタープライズ開発において、他の何かが単に悪いコーディングや怠惰であることを考えていたでしょう。ただし、クラスをサブクラス化しpublic、そのメソッドの1つをオーバーライドする必要がありました。

internal class SslStreamEx : System.Net.Security.SslStream
{
    public override void Close()
    {
        try
        {
            // Send close_notify manually
        }
        finally
        {
            base.Close();
        }
    }
}

メソッドは必須であり、Eric Lippertが言ったように、publicメソッドinternalを実際に設定する必要がある場合を除いて、メソッドを設定することには論理的な意味がないと考えるようになりました。

今までそれについて考えるのをやめたことは一度もありませんでした、それを受け入れましたが、エリックの投稿を読んだ後、本当に考えさせられ、多くの審議の結果、それは非常に理にかなっています。


0

違いはありません。このプロジェクトでは、多くのクラスを内部に作成しましたが、別のアセンブリでユニットテストを実行し、アセンブリ情報でInternalsVisibleToを使用して、UnitTestアセンブリが内部クラスを呼び出せるようにしました。内部クラスに内部コンストラクターがある場合、なんらかの理由で単体テストアセンブリでActivator.CreateInstanceを使用してインスタンスを作成できないことに気付きました。しかし、コンストラクタをパブリックに変更しても、クラスがまだ内部にある場合は、正常に機能します。しかし、これは非常にまれなケースだと思います(エリックが元の投稿で述べたように:リフレクション)。


0

私はこれについてさらに意見があると思います。最初は、内部クラスでパブリックに何かを宣言することがどのように理にかなっているのか疑問に思いました。それから私はここで終わりました、あなたが後でクラスをパブリックに変更することに決めたならそれは良いかもしれないと読んでいます。そうだね。だから、私の心に形成されたパターンそれが現在の動作を変更しない場合は、寛容にして、現在のコードの状態で意味をなさない(そして害を与えない)ことを許可しますが、後でそれを行う場合クラスの宣言を変更します。

このような:

public sealed class MyCurrentlySealedClass
{
    protected void MyCurretlyPrivateMethod()
    {
    }
}

上記の「パターン」によると、これは完全に問題ないはずです。それは同じ考えに従います。privateクラスを継承できないため、メソッドとして動作します。しかし、sealed制約を削除しても、それは有効です。継承されたクラスは、このメソッドを見ることができます。これは、私が達成したかったことです。しかし、警告が表示されます:CS0628、またはCA1047。どちらもクラスのprotectedメンバーを宣言しないでくださいsealed。さらに、私はそれが無意味であることについて完全に合意しました:「保護されたクラスの保護されたメンバー」警告(シングルトンクラス)

したがって、この警告とディスカッションがリンクされた後、私はすべてを内部クラスにするか、内部クラスにするかを決定しました。これは、そのような考え方に準拠し、異なる「パターン」を混在させないためです。


Eric Lippert氏は、この方法(または少なくとも理由により)を行うことを強く望んでいます。彼の記事は間違いなく読む価値がある。
bopapa_1979年
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.