Perché usare un metodo pubblico in una classe interna?


250

C'è molto codice in uno dei nostri progetti che assomiglia a questo:

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

        return $({s}Foo);
    }
}

C'è qualche motivo esplicito per farlo oltre a "è più facile rendere il tipo pubblico in un secondo momento?"

Sospetto che sia importante solo in casi molto strani (riflesso in Silverlight) o per niente.


1
Nella mia esperienza, questa è la pratica normale.
phoog,

2
@phoog, OK. ma perché è la pratica normale? Forse è più semplice cambiare un gruppo di metodi in interno? Correzione rapida -> selezionare invece il tipo interno?
bopapa_1979,

è quello che ho sempre ipotizzato. Non ho un'analisi ben ponderata, tuttavia, motivo per cui non ho inviato una risposta.
phoog,

2
La risposta di Eric Lippert riassume molto bene il mio pensiero e fa emergere anche qualcosa che si nascondeva nel mio cervello cercando di trovare una via d'uscita: le implementazioni implicite dei membri dell'interfaccia devono essere pubbliche.
phoog,

Questo metodo non è uguale return s + "Foo";? L' +operatore non si preoccupa delle stringhe nulle o vuote.
Mikkel R. Lund,

Risposte:


418

AGGIORNAMENTO: Questa domanda è stata l'oggetto del mio blog a settembre 2014 . Grazie per l'ottima domanda!

C'è un considerevole dibattito su questa domanda anche all'interno dello stesso team di compilatori.

Prima di tutto, è saggio capire le regole. Un membro pubblico di una classe o di una struttura è un membro accessibile a tutto ciò che può accedere al tipo contenente . Quindi un membro pubblico di una classe interna è effettivamente interno.

Quindi ora, data una classe interna, i suoi membri a cui desideri accedere nell'assemblea dovrebbero essere contrassegnati come pubblici o interni?

La mia opinione è: contrassegnare tali membri come pubblici.

Uso "pubblico" per indicare "questo membro non è un dettaglio di implementazione". Un membro protetto è un dettaglio di implementazione; c'è qualcosa che sarà necessario per far funzionare una classe derivata. Un membro interno è un dettaglio di implementazione; qualcos'altro interno a questo assembly ha bisogno del membro per funzionare correttamente. Un membro pubblico dice "questo membro rappresenta la chiave, funzionalità documentata fornita da questo oggetto".

Fondamentalmente, il mio atteggiamento è: supponiamo di aver deciso di trasformare questa classe interna in una classe pubblica. Per fare ciò, voglio cambiare esattamente una cosa : l'accessibilità della classe. Se trasformare una classe interna in una classe pubblica significa che devo anche trasformare un membro interno in un membro pubblico, quel membro faceva parte della superficie pubblica della classe e avrebbe dovuto essere pubblico in primo luogo.

Altre persone non sono d'accordo. C'è un contingente che dice che vogliono essere in grado di dare un'occhiata alla dichiarazione di un membro e sapere immediatamente se verrà chiamato solo dal codice interno.

Sfortunatamente, ciò non sempre funziona bene; ad esempio, una classe interna che implementa un'interfaccia interna deve ancora avere i membri di implementazione contrassegnati come pubblici, perché fanno parte della superficie pubblica della classe .


11
Mi piace questa risposta ... si adatta bene al mio atteggiamento verso la scrittura di codice che si auto documenta nella massima misura possibile, e l'ambito è un ottimo modo per trasmettere l'intento, specialmente se gli altri membri del team comprendono la tua convenzione.
bopapa_1979,

10
+1 per aver detto quello che volevo dire ma formulandolo molto meglio di quello che ero in grado (anche per far apparire l'angolo dell'interfaccia, anche se noto che è vero solo per le implementazioni implicite dei membri).
phoog,

2
La parte Interfaccia è importante: è impossibile implementare un metodo di interfaccia usando la dichiarazione di interfaccia interna, pubblica o esplicita. Quindi la parola pubblico è sovraccarica di due significati.
Michael Stum

8
Dal punto di vista idealogico, la tua tesi a favore publicè molto convincente. Tuttavia, trovo che "non sempre funziona bene ..." per essere particolarmente potente. La perdita di coerenza svaluta tutti i vantaggi "a colpo d'occhio". Usare internalin questo modo significa costruire intenzionalmente intuizioni che a volte sono sbagliate. Avere un'intuizione per lo più giusta è l'IMO una cosa orribile da programmare.
Brian,

3
Citazione importante dal tuo post sul blog: "Il mio consiglio è di discutere il problema tra il tuo team, prendere una decisione e poi attenerci."
bopapa_1979,

17

Se la classe lo è internal, non importa dal punto di vista dell'accessibilità se si contrassegna un metodo internalo public. Tuttavia è comunque utile utilizzare il tipo che useresti se la classe fossepublic .

Mentre alcuni hanno detto che questo facilita le transizioni da internal a public. Serve anche come parte della descrizione del metodo. Internali metodi sono generalmente considerati non sicuri per l'accesso illimitato, mentrepublic metodi sono considerati (principalmente) giochi gratuiti.

Usando internalo publiccome faresti in una publicclasse, ti assicuri di comunicare quale stile di accesso è previsto, facilitando al contempo il lavoro richiesto per rendere la classepublic in futuro.


Tendo a limitare tutto nella massima misura possibile mentre creo una buona API e consento al mio consumatore previsto di fare ciò che intendo fare. Sono anche con te nel segnare il membro come farei se la classe fosse pubblica. Più specificamente, contrassegnerei qualsiasi pubblico membro solo se lo considero sempre sicuro. Altrimenti qualcun altro che in seguito segnerà il pubblico della classe (succede) potrebbe esporre membri non sicuri.
bopapa_1979,

@Eric: Se vuoi rinunciare a determinare la sicurezza di un metodo fino a quando non sei pronto, va bene, ma mi riferivo solo a metodi ritenuti sicuri, nel qual caso non c'è esposizione.
Guvante,

10

Contraggo spesso i miei metodi in classi interne pubbliche anziché interne come a) non importa davvero b) uso interno per indicare che il metodo è intenzionalmente interno (c'è qualche motivo per cui non voglio esporre questo metodo in una classe pubblica. Pertanto, se ho un metodo interno, devo davvero capire il motivo per cui è interno prima di cambiarlo in pubblico, mentre se ho a che fare con un metodo pubblico in una classe interna, devo davvero pensare al perché la classe è interna rispetto al motivo per cui ogni metodo è interno.


Grazie per la risposta. Tendo a insistere ostinatamente sul fatto che "tutto" è importante durante la codifica :)
bopapa_1979,

1
Hai ragione Eric, è stato un commento un po 'buttato via. Spero che il resto della mia risposta sia stato di qualche utilità. Penso che la risposta di Eric Lippert sia quello che stavo cercando di esprimere, ma l'ha spiegato molto meglio.
Ɖiamond ǤeezeƦ

8

Ho il sospetto che "è più facile rendere il tipo pubblico in seguito?" è.

Le regole di scoping indicano che il metodo sarà visibile solo come internal- quindi non importa se i metodi sono contrassegnati publicointernal .

Una possibilità che viene in mente è che la classe era pubblica e successivamente è stata cambiata internale lo sviluppatore non si è preoccupato di cambiare tutti i modificatori dell'accessibilità del metodo.


1
+1 per inchiodare l'ovvio scenario "la classe pubblica è diventata interna".
bopapa_1979,

8

In alcuni casi, può anche essere che il tipo interno implementi un'interfaccia pubblica, il che significherebbe che tutti i metodi definiti su tale interfaccia dovrebbero ancora essere dichiarati pubblici.



0

internalafferma che è possibile accedere al membro solo dallo stesso assembly. Altre classi che assieme possono accedere al internal publicmembro, ma non sarebbe in grado di accedere a una privateo protectedmembro, internaloppure no.


Ma se i metodi fossero contrassegnati internalanziché public, la loro visibilità non cambierebbe. Credo che sia questo a chiedere l'OP.
Oded,

1
@zapthedingbat: il post è effettivamente corretto, ma in realtà risponde alla domanda?
Oded,

1
Bene, la domanda è PERCHÉ contrassegnarli in quel modo. Comprendo le basi dell'ambito. Grazie per il tentativo, però!
bopapa_1979,

0

In realtà ho lottato con questo oggi. Fino ad ora avrei detto che tutti i metodi dovevano essere contrassegnati internalse la classe fosse internale avrebbe considerato qualcos'altro semplicemente una cattiva codifica o pigrizia, specialmente nello sviluppo delle imprese; tuttavia, ho dovuto sottoclassare una publicclasse e sovrascrivere uno dei suoi metodi:

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

Il metodo DEVE essere publice mi ha fatto pensare che non ci sia davvero alcun punto logico per impostare metodi comeinternal se non lo fossero davvero, come ha detto Eric Lippert.

Fino ad ora non ho mai veramente smesso di pensarci, l'ho solo accettato, ma dopo aver letto il post di Eric mi ha fatto davvero pensare e dopo molte riflessioni ha molto senso.


0

C'è differenza. Nel nostro progetto abbiamo reso molte classi interne, ma eseguiamo unit test in un altro assembly e nelle nostre informazioni di assembly abbiamo utilizzato InternalsVisibleTo per consentire all'assembly UnitTest di chiamare le classi interne. Ho notato che se la classe interna ha un costruttore interno, non siamo in grado di creare istanze utilizzando Activator.CreateInstance nell'assemblaggio di unit test per qualche motivo. Ma se cambiamo il costruttore in pubblico ma la classe è ancora interna, funziona benissimo. Ma immagino che questo sia un caso molto raro (come ha detto Eric nel post originale: Reflection).


0

Penso di avere un'opinione aggiuntiva su questo. All'inizio, mi chiedevo come abbia senso dichiarare qualcosa al pubblico in una classe interna. Poi sono finito qui, leggendo che potrebbe essere bello se in seguito decidessi di cambiare la classe in pubblico. Vero. Quindi, un modello si è formato nella mia mente: se non cambia il comportamento attuale, allora sii permissivo e consenti cose che non hanno senso (e non fanno male) nello stato attuale del codice, ma in seguito lo farebbe, se tu cambia la dichiarazione della classe.

Come questo:

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

Secondo lo "schema" che ho menzionato sopra, questo dovrebbe andare perfettamente bene. Segue la stessa idea. Si comporta come un privatemetodo, poiché non è possibile ereditare la classe. Ma se elimini il sealedvincolo, è ancora valido: le classi ereditate possono vedere questo metodo, che è assolutamente ciò che volevo ottenere. Ma ricevi un avviso:, CS0628o CA1047. Entrambi stanno per non dichiarare protectedmembri in una sealedclasse. Inoltre, ho trovato il pieno accordo, sul fatto che è insensato: avviso "Membro protetto nella classe sigillata" (una classe singleton)

Quindi, dopo questo avvertimento e la discussione collegata, ho deciso di rendere tutto interno o meno, in una classe interna, perché si conforma più a quel tipo di pensiero e non mescoliamo "schemi" diversi.


Preferisco drasticamente farlo nel modo (o almeno per i motivi), ha detto Eric Lippert. Il suo articolo merita sicuramente una lettura.
bopapa_1979,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.