Warum eine öffentliche Methode in einer internen Klasse verwenden?

250

In einem unserer Projekte steckt viel Code, der so aussieht:

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

        return $({s}Foo);
    }
}

Gibt es einen anderen expliziten Grund als "Es ist einfacher, den Typ später zu veröffentlichen?"

Ich vermute, dass es nur in sehr seltsamen Randfällen (Reflexion in Silverlight) oder überhaupt nicht wichtig ist.

bopapa_1979
quelle
1
Nach meiner Erfahrung ist das die normale Praxis.
Phoog
2
@ Phoog, OK. aber warum ist es die normale Praxis? Vielleicht einfacher als eine Reihe von Methoden auf intern umzustellen? Schnellkorrektur -> stattdessen den Typ intern markieren?
Bopapa_1979
das habe ich immer angenommen. Ich habe jedoch keine gut durchdachte Analyse, weshalb ich keine Antwort gepostet habe.
Phoog
2
Eric Lipperts Antwort fasst mein Denken sehr gut zusammen und bringt auch etwas auf den Punkt, das in meinem Gehirn lauerte, um einen Ausweg zu finden: Implizite Implementierungen von Schnittstellenmitgliedern müssen öffentlich sein.
Phoog
Ist diese Methode nicht gleich return s + "Foo";? Der +Operator kümmert sich nicht um null oder leere Zeichenfolgen.
Mikkel R. Lund

Antworten:

418

UPDATE: Diese Frage war das Thema meines Blogs im September 2014 . Danke für die tolle Frage!

Selbst innerhalb des Compilerteams selbst gibt es erhebliche Debatten zu dieser Frage.

Zunächst einmal ist es ratsam, die Regeln zu verstehen. Ein öffentliches Mitglied einer Klasse oder Struktur ist ein Mitglied, auf das jeder zugreifen kann, der auf den enthaltenen Typ zugreifen kann . Ein öffentliches Mitglied einer internen Klasse ist also effektiv intern.

Sollten nun bei einer internen Klasse die Mitglieder, auf die Sie in der Versammlung zugreifen möchten, als öffentlich oder intern gekennzeichnet sein?

Meine Meinung ist: Markiere solche Mitglieder als öffentlich.

Ich benutze "public", um zu bedeuten, dass "dieses Mitglied kein Implementierungsdetail ist". Ein geschütztes Mitglied ist ein Implementierungsdetail. Es gibt etwas, das benötigt wird, damit eine abgeleitete Klasse funktioniert. Ein internes Mitglied ist ein Implementierungsdetail. Für etwas anderes in dieser Baugruppe ist das Mitglied erforderlich, damit es ordnungsgemäß funktioniert. Ein öffentliches Mitglied sagt: "Dieses Mitglied repräsentiert die wichtigsten dokumentierten Funktionen, die von diesem Objekt bereitgestellt werden."

Grundsätzlich lautet meine Einstellung: Angenommen, ich habe beschlossen, diese interne Klasse zu einer öffentlichen Klasse zu machen. Dazu möchte ich genau eines ändern : die Zugänglichkeit der Klasse. Wenn das Verwandeln einer internen Klasse in eine öffentliche Klasse bedeutet, dass ich auch ein internes Mitglied in ein öffentliches Mitglied verwandeln muss, dann war dieses Mitglied Teil der öffentlichen Fläche der Klasse und sollte an erster Stelle öffentlich sein.

Andere Leute sind anderer Meinung. Es gibt ein Kontingent, das besagt, dass sie in der Lage sein möchten, einen Blick auf die Erklärung eines Mitglieds zu werfen und sofort zu wissen, ob sie nur aus dem internen Code aufgerufen wird.

Leider klappt das nicht immer gut; Bei einer internen Klasse, die eine interne Schnittstelle implementiert, müssen die implementierenden Mitglieder weiterhin als öffentlich markiert sein, da sie Teil der öffentlichen Oberfläche der Klasse sind .

Eric Lippert
quelle
11
Ich mag diese Antwort ... sie passt gut zu meiner Einstellung, Code zu schreiben, der sich so weit wie möglich selbst dokumentiert, und der Umfang ist eine großartige Möglichkeit, Absichten zu verbreiten, insbesondere wenn Ihre anderen Teammitglieder Ihre Konvention verstehen.
Bopapa_1979
10
+1, um zu sagen, was ich sagen wollte, aber es viel besser zu formulieren, als ich konnte (auch, um den Schnittstellenwinkel zu erhöhen, obwohl ich feststelle, dass dies nur für implizite Mitgliederimplementierungen gilt).
Phoog
2
Der Schnittstellenteil ist wichtig - es ist unmöglich, eine Schnittstellenmethode mit internen, entweder öffentlichen oder expliziten Schnittstellendeklarationen zu implementieren. Das Wort public ist also mit zwei Bedeutungen überladen.
Michael Stum
8
Aus idealogischer Sicht ist Ihr Argument dafür publicsehr überzeugend. Allerdings finde ich das "funktioniert nicht immer gut ..." besonders mächtig. Wenn Sie die Konsistenz verlieren, werden alle Vorteile auf einen Blick abgewertet. Auf internaldiese Weise zu verwenden bedeutet, absichtlich Intuitionen aufzubauen, die manchmal falsch sind. Eine meist richtige Intuition zu haben, ist IMO eine schreckliche Sache für die Programmierung.
Brian
3
Wichtiges Zitat aus Ihrem Blog-Beitrag: "Mein Rat ist, das Problem in Ihrem Team zu diskutieren, eine Entscheidung zu treffen und sich dann daran zu halten."
Bopapa_1979
17

Wenn dies der internalFall ist , spielt es unter dem Gesichtspunkt der Barrierefreiheit keine Rolle, ob Sie eine Methode markieren internaloder public. Es ist jedoch immer noch gut, den Typ zu verwenden, den Sie verwenden würden, wenn die Klasse wäre public.

Während einige gesagt haben, dass dies Übergänge von internalnach erleichtert public. Es dient auch als Teil der Beschreibung der Methode. InternalMethoden werden normalerweise als unsicher für uneingeschränkten Zugriff angesehen, während publicMethoden als (meistens) freies Spiel angesehen werden.

Indem Sie internaloder publicwie in einer publicKlasse verwenden, stellen Sie sicher, dass Sie kommunizieren, welche Art des Zugriffs erwartet wird, und erleichtern gleichzeitig die Arbeit, die erforderlich ist, um die Klasse publicin Zukunft zu erstellen.

Guvante
quelle
Ich neige dazu, alles so weit wie möglich einzuschränken, während ich eine gute API erstelle und meinem beabsichtigten Verbraucher erlaube, das zu tun, was ich von ihm beabsichtige. Ich bin auch bei Ihnen, um das Mitglied so zu markieren, als ob die Klasse öffentlich wäre. Insbesondere würde ich jedes Mitglied nur dann als öffentlich markieren, wenn ich es für immer sicher halte. Andernfalls könnte jemand anderes, der die Klasse später als öffentlich markiert (es passiert), nicht sichere Mitglieder aussetzen.
Bopapa_1979
@Eric: Wenn Sie darauf verzichten möchten, die Sicherheit einer Methode zu bestimmen, bis Sie bereit sind, ist das in Ordnung, aber ich habe mich nur auf Methoden bezogen, die als sicher gelten. In diesem Fall gibt es keine Belichtung.
Guvante
10

Ich markiere meine Methoden oft in internen Klassen öffentlich statt intern als a) es ist nicht wirklich wichtig und b) ich benutze intern, um anzuzeigen, dass die Methode absichtlich intern ist (es gibt einen Grund, warum ich dies nicht offenlegen möchte Methode in einer öffentlichen Klasse. Wenn ich also eine interne Methode habe, muss ich wirklich verstehen, warum sie intern ist, bevor ich sie in öffentlich umwandle. Wenn ich mich mit einer öffentlichen Methode in einer internen Klasse befasse, muss ich wirklich darüber nachdenken, warum die Klasse ist intern im Gegensatz dazu, warum jede Methode intern ist.

Ɖiamond ǤeezeƦ
quelle
Danke für die Antwort. Ich neige dazu, hartnäckig darauf zu bestehen, dass "alles" beim Codieren
wichtig ist
1
Sie haben Recht, Eric, es war ein kleiner Kommentar. Ich hoffe, dass der Rest meiner Antwort von Nutzen war. Ich denke, Eric Lipperts Antwort ist das, was ich auszudrücken versuchte, aber er hat es so viel besser erklärt.
Ǥiamond ǤeezeƦ
8

Ich vermute, dass "es einfacher ist, den Typ später zu veröffentlichen?" ist es.

Die Scoping-Regeln bedeuten, dass die Methode nur als sichtbar ist internal- es spielt also keine Rolle, ob die Methoden markiert sind publicoder nicht internal.

Eine Möglichkeit , die in den Sinn kommt , ist , dass die Klasse war öffentlich und wurde später geändert internalund der Entwickler nicht die Mühe , alle Verfahrens Zugänglichkeit Modifikatoren zu ändern.

Oded
quelle
1
+1 für das offensichtliche Szenario "öffentliche Klasse wurde intern".
Bopapa_1979
8

In einigen Fällen kann es auch sein, dass der interne Typ eine öffentliche Schnittstelle implementiert, was bedeutet, dass alle auf dieser Schnittstelle definierten Methoden weiterhin als öffentlich deklariert werden müssen.

Trevor Pilley
quelle
2

Es ist dasselbe, die öffentliche Methode wird wirklich als intern markiert, da sie sich innerhalb einer internen Klasse befindet, aber sie hat einen Vorteil (wie Sie zu Gast hatten). Wenn Sie die Klasse als öffentlich markieren möchten, müssen Sie weniger Code ändern.

Mario Corchero
quelle
2
Es gibt bereits eine verwandte Frage: stackoverflow.com/questions/711361/…
Mario Corchero
0

internalsagt, dass auf das Mitglied nur innerhalb derselben Assembly zugegriffen werden kann. Andere Klassen in dieser Assembly können auf das internal publicMitglied zugreifen , können jedoch nicht auf ein privateoder protectedMitglied zugreifen internaloder nicht.

Ryan P.
quelle
Wenn jedoch Methoden internalanstelle von markiert publicwürden, würde sich ihre Sichtbarkeit nicht ändern. Ich glaube, darum bittet das OP.
Oded
1
@zapthedingbat - der Beitrag ist sachlich korrekt, aber beantwortet er tatsächlich die Frage?
Oded
1
Richtig, die Frage ist, WARUM sie so markieren. Ich verstehe die Grundlagen des Umfangs. Vielen Dank für den Versuch!
Bopapa_1979
0

Ich hatte heute tatsächlich damit zu kämpfen. Bis jetzt hätte ich gesagt, dass alle Methoden markiert werden sollten, internalwenn die Klasse internaletwas anderes als schlechte Codierung oder Faulheit betrachtet hätte und hätte, insbesondere in der Unternehmensentwicklung; Ich musste jedoch eine publicKlasse unterordnen und eine ihrer Methoden überschreiben:

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

Die Methode MUSS sein publicund es hat mich zu dem Gedanken gebracht, dass es wirklich keinen logischen Grund gibt, Methoden so internaleinzustellen, als ob sie wirklich sein müssten, wie Eric Lippert sagte.

Bis jetzt habe ich nie wirklich aufgehört darüber nachzudenken, ich habe es einfach akzeptiert, aber nachdem ich Erics Beitrag gelesen hatte, brachte es mich wirklich zum Nachdenken und nach vielen Überlegungen macht es sehr viel Sinn.

Sturm
quelle
0

Es gibt einen Unterschied. In unserem Projekt haben wir viele interne Klassen erstellt, aber wir führen Unit-Tests in einer anderen Assembly durch. In unseren Assembly-Informationen haben wir InternalsVisibleTo verwendet, damit die UnitTest-Assembly die internen Klassen aufrufen kann. Ich habe festgestellt, dass die interne Klasse, wenn sie einen internen Konstruktor hat, aus irgendeinem Grund keine Instanz mit Activator.CreateInstance in der Unit-Test-Assembly erstellen kann. Wenn wir den Konstruktor jedoch in public ändern, die Klasse jedoch noch intern ist, funktioniert dies einwandfrei. Aber ich denke, dies ist ein sehr seltener Fall (wie Eric im ursprünglichen Beitrag sagte: Reflection).

Farrah Jiang
quelle
0

Ich glaube, ich habe eine zusätzliche Meinung dazu. Zuerst habe ich mich gefragt, wie es sinnvoll ist, in einer internen Klasse etwas öffentlich zu erklären. Dann bin ich hier gelandet und habe gelesen, dass es gut sein könnte, wenn Sie später beschließen, die Klasse in öffentlich zu ändern. Wahr. Also ein Muster, das sich in meinem Kopf gebildet hat: Wenn es das aktuelle Verhalten nicht ändert, dann sei freizügig und erlaube Dinge, die im aktuellen Code-Status keinen Sinn ergeben (und nicht schaden), aber später, wenn du es tust Ändern Sie die Deklaration der Klasse.

So was:

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

Nach dem oben erwähnten "Muster" sollte dies vollkommen in Ordnung sein. Es folgt der gleichen Idee. Es verhält sich wie eine privateMethode, da Sie die Klasse nicht erben können. Wenn Sie die sealedEinschränkung jedoch löschen , ist sie weiterhin gültig: Die geerbten Klassen können diese Methode sehen, was absolut das ist, was ich erreichen wollte. Aber du bekommst eine Warnung: CS0628oder CA1047. Bei beiden geht es darum, keine protectedMitglieder einer sealedKlasse zu deklarieren . Darüber hinaus habe ich volle Übereinstimmung darüber gefunden, dass es sinnlos ist: Warnung "Geschütztes Mitglied in versiegelter Klasse" (eine Singleton-Klasse)

Nach dieser Warnung und der damit verbundenen Diskussion habe ich beschlossen, alles intern oder weniger in einer internen Klasse zu machen, da es mehr dieser Art des Denkens entspricht und wir keine unterschiedlichen "Muster" mischen.

Hix
quelle
Ich ziehe es drastisch vor, es so zu machen (oder zumindest aus den Gründen), sagte Eric Lippert. Sein Artikel ist definitiv eine Lektüre wert.
bopapa_1979