Was ist der Anwendungsfall für den Modifikator "privat geschützt" (C # 7.2)?

70

C # 7.2 führt den privaten geschützten Modifikator ein .

Ich habe den Zugriff auf Felder mit Eigenschaften immer geschützt und den Zugriff über die Get / Set-Methoden ermöglicht, da ich normalerweise nicht möchte, dass der interne Status meines Objekts durch etwas anderes als meine eigene Klasse geändert wird.

Ich versuche zu verstehen, warum das C # -Sprachteam diese Funktion hinzugefügt hat. Nach einer umfangreichen Suche auf Google und dem Lesen und Ansehen der "Was ist neu" -Medien (ich habe die Pressemitteilung , Details und Videos von Mads Torgerson gesehen ) bin ich immer noch nicht klüger.

Für mich scheint dies einem Entwickler zu ermöglichen, das Liskov-Substitutionsprinzip zu brechen, aber dies kann daran liegen, dass ich nicht verstehe, warum diese Funktion jetzt existiert.

Ich verstehe, wie es verwendet werden kann, nur nicht warum - kann jemand bitte ein reales Verwendungsbeispiel anstelle des erfundenen in den MSDN-Dokumenten bereitstellen?

Jay
quelle
4
Sind Sie sicher, dass Sie verstehen, was dieser Modifikator tatsächlich tut?
Evk
1
Es ist sehr ähnlich, protectedaber mit einem wichtigen Unterschied: Wenn eine Klasse davon abgeleitet ist, aber in einer anderen Assembly, kann sie nicht auf dieses Mitglied zugreifen, obwohl protectedes nicht auf dieselbe Assembly beschränkt ist.
Tim Schmelter
6
Hier ist ein Beispiel, wo ich es verwenden möchte, wenn es Hilfe gibt: github.com/GoogleCloudPlatform/google-cloud-dotnet/blob/master/…
Jon Skeet
1
@Evk: Weil ich tun möchte es instanziiert direkt aus Abfrage. Es ist in Ordnung, dass Query eine konkrete Klasse ist, und es ist in Ordnung, eine Unterklasse zu haben - aber nur innerhalb derselben Assembly. Es ist nicht in Ordnung, wenn anderer Code Instanzen von erstellt Query. private protectedwürde mir genau das geben was ich will.
Jon Skeet
2
Unter blogs.msdn.microsoft.com/ericlippert/2008/04/24/… finden Sie einige Gedanken dazu. Es gibt einen Grund, warum es fünfzehn Jahre gedauert hat, bis das Feature nahe genug an der Spitze der Prioritätenliste stand, um implementiert zu werden: Es ist kein sehr überzeugendes, interessantes oder nützliches Feature .
Eric Lippert

Antworten:

77

Vor C # 7.2 hatten wir protected internalModifikator. Dies bedeutet wirklich, dass geschützt ODER intern ist, dh, das Mitglied Aist für untergeordnete Klassen und auch für jede Klasse in der aktuellen Assembly zugänglich, selbst wenn diese Klasse kein Kind der Klasse ist A(daher wird die durch "geschützt" implizierte Einschränkung gelockert).

private protectedbedeutet wirklich geschützt und intern. Das heißt, das Mitglied ist nur für untergeordnete Klassen zugänglich, die sich in derselben Assembly befinden, nicht jedoch für untergeordnete Klassen, die sich außerhalb der Assembly befinden (daher wird die durch "geschützt" implizierte Einschränkung eingeschränkt - wird noch restriktiver). Dies ist nützlich, wenn Sie eine Klassenhierarchie in Ihrer Assembly erstellen und nicht möchten, dass untergeordnete Klassen aus anderen Assemblys auf bestimmte Teile dieser Hierarchie zugreifen.

Wir können annehmen Beispiel , dass Jon Skeet vorgesehen in den Kommentaren . Angenommen, Sie haben Klasse

public class MyClass {

}

Und Sie möchten in der Lage sein, nur in der aktuellen Assembly davon zu erben, möchten aber nicht zulassen, dass diese Klasse direkt instanziiert wird, außer innerhalb dieser Klassenhierarchie.

Eine Vererbung nur innerhalb der aktuellen Baugruppe kann mit einem internen Konstruktor erreicht werden

public class MyClass {
    internal MyClass() {
    }
}

Das Verhindern einer direkten Instanziierung mit Ausnahme der aktuellen Klassenhierarchie kann mit dem geschützten Konstruktor erreicht werden:

public class MyClass {
    protected MyClass() {
    }
}

Und um beides zu bekommen, benötigen Sie einen private protectedKonstruktor:

public class MyClass {
    private protected MyClass() {
    }
}
Evk
quelle
1
@ TimSchmelter Sie ähneln sich in gewissem Sinne. Sie beinhalten sowohl Einschränkungen hinsichtlich untergeordneter Klassen (geschützt) als auch hinsichtlich der aktuellen Assembly (intern). Sie kombinieren diese Einschränkungen nur auf andere Weise (und vs oder).
Evk
Sorry, habe meinen Kommentar gelöscht, weil meinungsbasiert. Aber ich denke immer noch, dass protecteddas "ähnlicher" ist als protected internal(sehr verwirrender Zugriffsmodifikator, weil die öffentliche Versammlung weise ist). Der protectedZugriffsmodifikator hat denselben Vertrag: Nur innerhalb desselben Typs oder der daraus abgeleiteten Typen zugänglich. Der Umfang ist jedoch nicht auf diese Baugruppe beschränkt private internal. Wenn es sich also um ein Mitglied handelt protected, müssen Sie jedoch vermeiden, dass es von außerhalb dieser benötigten Assembly zugänglich ist private protected.
Tim Schmelter
So viel habe ich beim Lesen der MSDN-Seite ziemlich gut verstanden . Was ich nicht verstehe , ist, warum Sie jemals das tun möchten, was sie beschreiben - nämlich auf ein Feld in einer Basisklasse aus der abgeleiteten Klasse (in derselben Assembly) zuzugreifen, ohne eine Eigenschaft einzuführen. Wenn das Beispiel so belassen wurde, wie es bei Ihnen ist, ist es absolut sinnvoll (und mir hat gefallen, wie Sie den Unterschied zwischen geschützt intern und privat geschützt beschreiben). Vielleicht bin ich nur voreingenommen gegen das MSDN-Beispiel!
Jay
@Jay Nun, Sie sollten von MSDN-Beispielen nicht viel Sinn für die reale Welt erwarten (wie ich verstehe, ist Ihre Hauptbeschwerde, dass sie geschützte Felder verwenden, die nicht einmal damit zusammenhängen, ob sie nur geschützt oder "privat geschützt" sind). Schließlich ist es kein Beispiel für erstklassige Designpraktiken - es zeigt nur, was "privat geschützt" tut.
Evk
1
@RuudLenders: Dieses Argument hat lange gedauert; Ich habe mich selbst für "proternal" gehalten, aber aus irgendeinem Grund wurde das für gottesfürchtig gehalten. Sie und alle anderen wurden eingeladen, am Designprozess teilzunehmen. Niemand hat sich etwas Besseres ausgedacht, und anscheinend haben Sie es auch nicht. Weitere Informationen finden Sie unter roslyn.codeplex.com/discussions/541194 .
Eric Lippert
19

Für Zwei-Wort- Zugriffsmodifikatoren habe ich dieses Konzept - der erste Accessor bezieht sich auf eine andere Assembly, der zweite auf die Assembly, in der er definiert wurde.

intern geschützt

  • geschützt in einer anderen Assembly: nur in den untergeordneten Klassen zugänglich.

  • intern in der aktuellen Assembly: Zugriff für alle in der aktuellen Assembly.

privat geschützt

  • privat in einer anderen Assembly: ist nicht zugänglich.
  • In der aktuellen Assembly geschützt : Nur in den untergeordneten Klassen verfügbar.
Suren Srapyan
quelle
1
Super, es ist wirklich einfach und macht wirklich Sinn. Es half mir, die Idee von all dieser verwirrenden Sache zu bekommen. Vielen Dank!
Andrew Dashkov
8

Nehmen wir an, Sie haben eine interne Klasse namens SomeHelper, die Sie als Teil der Implementierung einer öffentlichen abstrakten Basisklasse verwenden möchten:

public abstract class Test
{
    // Won't compile because SomeHelper is internal.
    protected SomeHelper CreateHelper()
    {
        return new SomeHelper();
    }

    public int Func(int x)
    {
        var helper = CreateHelper();
        return helper.DoSomething(x);
    }
}

internal class SomeHelper
{
    public virtual int DoSomething(int x)
    {
        return -x;
    }
}

Dies wird nicht kompiliert, da keine geschützte Methode einen internen Typ zurückgeben kann. Ihre einzige SomeHelperMöglichkeit besteht darin, diese nicht zu verwenden oder SomeHelperöffentlich zu machen .

(Sie könnten SomeHelpereine geschützte innere Klasse von erstellen Test, aber das funktioniert nicht, wenn SomeHelperes für andere Klassen vorgesehen ist, die nicht von der Basisklasse abgeleitet sind.)

Mit der Einführung der private protectedFunktion können Sie Folgendes deklarieren CreateHelper():

private protected SomeHelper CreateHelper()
{
    return new SomeHelper();
}

Jetzt wird es kompiliert und Sie müssen Ihre Interna nicht mehr verfügbar machen.

Matthew Watson
quelle
1
Sie können Ihre SomeHelperals protectedinnere Klasse von delcare Test, dann wird es kompiliert (über "Ihr einziger Rückgriff ...").
Evk
@Evk Ah ja, ich werde das hinzufügen (aber dann wird es SomeHelpernatürlich nicht "auf diese Weise" verwendet, was ich wirklich gemeint habe).
Matthew Watson
Dies kann auch sehr nützlich sein
NSGaga-meist-inaktiv
Nicht "so gut". Dies ist in der Tat das reale Anwendungsbeispiel. Das Übergeben und Zurückgeben interner Typen an / von meinen eigenen hierarchiegeschützten Methoden ist seit vielen Jahren ein Problem. Die Problemumgehung bestand darin, solche Methoden als internallogisch zu kennzeichnen protectedund Codierungsdisziplin zu verwenden, um nicht von außerhalb der Hierarchie auf sie zuzugreifen. Das in der Antwort von @Evk bereitgestellte Beispiel verursachte niemals Probleme, da es gelöst wird, indem die Basisklasse abstractnur mit internalKonstruktoren erstellt wird.
Ivan Stoev
@IvanStoev beachten Sie, dass es nicht mein Beispiel ist, sondern aus dem realen Projekt von Jon Skeet stammt. Die Basisklasse kann in diesem Fall nicht abstrakt sein, da Sie sie instanziieren müssen (jedoch nur aus dieser Klasse und diesen Unterklassen).
Evk