Potresti spiegare qual è l'utilizzo pratico della internal
parola chiave in C #?
So che il internal
modificatore limita l'accesso all'assembly corrente, ma quando e in quali circostanze dovrei usarlo?
Potresti spiegare qual è l'utilizzo pratico della internal
parola chiave in C #?
So che il internal
modificatore limita l'accesso all'assembly corrente, ma quando e in quali circostanze dovrei usarlo?
Risposte:
Classi / metodi di utilità o di supporto a cui si desidera accedere da molte altre classi all'interno dello stesso assembly, ma a cui si desidera garantire che il codice in altri assembly non possa accedervi.
Da MSDN (tramite archive.org):
Un uso comune dell'accesso interno è nello sviluppo basato su componenti perché consente a un gruppo di componenti di cooperare in modo privato senza essere esposti al resto del codice dell'applicazione. Ad esempio, un framework per la creazione di interfacce utente grafiche potrebbe fornire classi Control e Form che cooperano utilizzando membri con accesso interno. Poiché questi membri sono interni, non sono esposti al codice che utilizza il framework.
È inoltre possibile utilizzare il modificatore interno insieme InternalsVisibleTo
all'attributo a livello di assieme per creare assiemi "amici" a cui viene concesso un accesso speciale alle classi interne dell'assieme di destinazione.
Ciò può essere utile per la creazione di assiemi di unit testing che possono quindi chiamare i membri interni dell'assembly da testare. Naturalmente a nessun altro assembly viene concesso questo livello di accesso, quindi quando si rilascia il sistema, l'incapsulamento viene mantenuto.
Se Bob ha bisogno di BigImportantClass, allora Bob ha bisogno di far iscrivere le persone che possiedono il progetto A per garantire che BigImportantClass sarà scritto per soddisfare le sue esigenze, testato per assicurarsi che soddisfi le sue esigenze, sia documentato come conforme alle sue esigenze e che un processo sarà messo in atto per garantire che non sarà mai cambiato in modo da non soddisfare più le sue esigenze.
Se una classe è interna, non deve passare attraverso quel processo, il che consente di risparmiare budget per il Progetto A che possono spendere per altre cose.
Il punto di interno non è che rende la vita difficile a Bob. È che ti consente di controllare le costose promesse fatte dal Progetto A in merito a funzionalità, durata, compatibilità e così via.
Un altro motivo per usare internal è se offuschi i tuoi binari. L'offuscatore sa che è sicuro confondere il nome della classe di qualsiasi classe interna, mentre il nome delle classi pubbliche non può essere criptato, perché ciò potrebbe spezzare i riferimenti esistenti.
Se stai scrivendo una DLL che incapsula una tonnellata di funzionalità complesse in una semplice API pubblica, viene utilizzato "interno" sui membri della classe che non devono essere esposti pubblicamente.
Nascondere la complessità (aka incapsulamento) è il concetto principale di ingegneria del software di qualità.
La parola chiave interna è molto utilizzata quando si crea un wrapper su codice non gestito.
Quando si dispone di una libreria basata su C / C ++ che si desidera importare, è possibile importare queste funzioni come funzioni statiche di una classe e renderle interne, in modo che l'utente abbia accesso al proprio wrapper e non all'API originale, quindi non può pasticciare con qualsiasi cosa. Le funzioni statiche possono essere utilizzate ovunque nell'assieme, per le classi di wrapper multiple necessarie.
Puoi dare un'occhiata a Mono.Cairo, è un wrapper per la libreria cairo che utilizza questo approccio.
Essendo guidato dalla regola "usa il modificatore più rigoroso che puoi" uso interno ovunque devo dire, per esempio, il metodo da un'altra classe fino a quando non ho esplicitamente bisogno di accedervi da un altro assembly.
Poiché l'interfaccia di assemblaggio è di solito più stretta della somma delle sue interfacce di classi, ci sono molti posti in cui la uso.
Trovo interno molto abusato. non dovresti davvero esporre determinate funzionalità solo a determinate classi che non vorresti ad altri consumatori.
Questo secondo me rompe l'interfaccia, rompe l'astrazione. Questo non vuol dire che non dovrebbe mai essere usato, ma una soluzione migliore è quella di refactoring a una classe diversa o di essere usato in un modo diverso, se possibile. Tuttavia, questo potrebbe non essere sempre possibile.
La ragione per cui può causare problemi è che un altro sviluppatore potrebbe essere incaricato di costruire un'altra classe nello stesso assembly in cui si trova il tuo. Avere degli interni riduce la chiarezza dell'astrazione e può causare problemi se usato in modo improprio. Sarebbe lo stesso problema come se lo rendessi pubblico. L'altra classe che viene creata dall'altro sviluppatore è ancora un consumatore, proprio come qualsiasi classe esterna. L'astrazione e l'incapsulamento delle classi non sono solo per la protezione di / da classi esterne, ma per tutte le classi.
Un altro problema è che molti sviluppatori penseranno che potrebbe essere necessario usarlo altrove nell'assemblaggio e contrassegnarlo comunque come interno, anche se al momento non ne hanno bisogno. Un altro sviluppatore potrebbe pensare che sia lì per la presa. In genere si desidera contrassegnare come privato fino a quando non si ha un'esigenza definitiva.
Ma un po 'di questo può essere soggettivo e non sto dicendo che non dovrebbe mai essere usato. Basta usare quando necessario.
Ne ho visto uno interessante l'altro giorno, forse la settimana, su un blog che non ricordo. Fondamentalmente non posso prendermi il merito per questo, ma ho pensato che potesse avere qualche utile applicazione.
Supponi di volere che una classe astratta venga vista da un'altra assemblea, ma non vuoi che qualcuno possa ereditarla. Sealed non funzionerà perché è astratto per una ragione, altre classi in quell'assembly ne ereditano. Privato non funzionerà perché potresti voler dichiarare una classe Parent da qualche parte nell'altro assembly.
spazio dei nomi Base.Assembly { genitore di classe astratta pubblica { vuoto astratto interno SomeMethod (); } // Funziona bene dato che si trova nello stesso assieme. classe pubblica ChildWithin: Parent { override interno void SomeMethod () { } } } spazio dei nomi Another.Assembly { // Kaboom, perché non è possibile ignorare un metodo interno classe pubblica ChildOutside: Parent { } Test di classe pubblica { //Va bene genitore privato _parent; test pubblico () { //Ancora bene _parent = new ChildWithin (); } } }
Come puoi vedere, consente effettivamente a qualcuno di utilizzare la classe Parent senza essere in grado di ereditare.
Questo esempio contiene due file: Assembly1.cs e Assembly2.cs. Il primo file contiene una classe base interna, BaseClass. Nel secondo file, un tentativo di creare un'istanza di BaseClass produrrà un errore.
// Assembly1.cs
// compile with: /target:library
internal class BaseClass
{
public static int intM = 0;
}
// Assembly1_a.cs
// compile with: /reference:Assembly1.dll
class TestAccess
{
static void Main()
{
BaseClass myBase = new BaseClass(); // CS0122
}
}
In questo esempio, utilizzare gli stessi file utilizzati nell'esempio 1 e modificare il livello di accessibilità di BaseClass in pubblico . Modificare anche il livello di accessibilità del membro IntM in interno . In questo caso, è possibile creare un'istanza della classe, ma non è possibile accedere al membro interno.
// Assembly2.cs
// compile with: /target:library
public class BaseClass
{
internal static int intM = 0;
}
// Assembly2_a.cs
// compile with: /reference:Assembly1.dll
public class TestAccess
{
static void Main()
{
BaseClass myBase = new BaseClass(); // Ok.
BaseClass.intM = 444; // CS0117
}
}
fonte : http://msdn.microsoft.com/en-us/library/7c5ka91b(VS.80).aspx
Quando si hanno metodi, classi, ecc. Che devono essere accessibili nell'ambito dell'assembly corrente e mai al di fuori di esso.
Ad esempio, un DAL può avere un ORM ma gli oggetti non devono essere esposti al livello aziendale. Tutte le interazioni devono essere eseguite tramite metodi statici e passando i parametri richiesti.
Un uso molto interessante di internal - con il membro interno ovviamente limitato solo all'assemblea in cui viene dichiarato - sta ottenendo in qualche modo la funzionalità "amico". Un membro amico è qualcosa che è visibile solo a certe altre assemblee al di fuori dell'assemblea in cui è stata dichiarata. C # non ha supporto incorporato per gli amici, tuttavia il CLR lo fa.
È possibile utilizzare InternalsVisibleToAttribute per dichiarare un'assemblea di amici e tutti i riferimenti all'interno dell'assemblea di amici tratteranno i membri interni della propria assemblea dichiarante come pubblici nell'ambito di tale assemblea. Un problema è che tutti i membri interni sono visibili; non puoi scegliere.
Un buon uso per InternalsVisibleTo è quello di esporre vari membri interni a un gruppo di test unitari eliminando così la necessità di complesse soluzioni di riflessione per testare quei membri. La visibilità di tutti i membri interni non è un grosso problema, tuttavia adottare questo approccio compromette le interfacce della classe piuttosto pesantemente e può potenzialmente rovinare l'incapsulamento all'interno dell'assemblea dichiarante.
Come regola empirica ci sono due tipi di membri:
Riduzione del rumore, meno tipi si espongono, più semplice è la libreria. La prova di manomissione / Sicurezza è un'altra (sebbene Reflection possa vincere contro di essa).
Le classi interne consentono di limitare l'API dell'assembly. Questo ha dei vantaggi, come semplificare la comprensione dell'API.
Inoltre, se esiste un bug nel proprio assieme, c'è meno possibilità che la correzione introduca una rottura. Senza le classi interne, dovresti presumere che cambiare i membri pubblici di qualsiasi classe sarebbe un cambiamento decisivo. Con le classi interne, puoi supporre che la modifica dei loro membri pubblici interrompa solo l'API interna dell'assembly (e tutti gli assembly a cui fa riferimento l'attributo InternalsVisibleTo).
Mi piace avere l'incapsulamento a livello di classe e di assemblaggio. Ci sono alcuni che non sono d'accordo con questo, ma è bello sapere che la funzionalità è disponibile.
Ho un progetto che utilizza LINQ-to-SQL per il back-end dei dati. Ho due spazi dei nomi principali: Biz e Data. Il modello di dati LINQ risiede nei dati ed è contrassegnato come "interno"; lo spazio dei nomi Biz ha classi pubbliche che avvolgono le classi di dati LINQ.
Quindi c'è Data.Client
, e Biz.Client
; quest'ultimo espone tutte le proprietà pertinenti dell'oggetto dati, ad esempio:
private Data.Client _client;
public int Id { get { return _client.Id; } set { _client.Id = value; } }
Gli oggetti Biz hanno un costruttore privato (per forzare l'uso dei metodi di fabbrica) e un costruttore interno che assomiglia a questo:
internal Client(Data.Client client) {
this._client = client;
}
Ciò può essere utilizzato da qualsiasi classe aziendale nella libreria, ma il front-end (UI) non ha modo di accedere direttamente al modello di dati, garantendo che il livello aziendale agisca sempre come intermediario.
Questa è la prima volta che uso davvero internal
molto e si sta dimostrando abbastanza utile.
Ci sono casi in cui ha senso rendere membri delle classi internal
. Un esempio potrebbe essere se si desidera controllare il modo in cui le classi vengono istanziate; diciamo che fornisci una sorta di factory per la creazione di istanze della classe. È possibile creare il costruttore internal
, in modo che la factory (che risiede nello stesso assembly) possa creare istanze della classe, ma il codice al di fuori di tale assembly non può.
Tuttavia, non riesco a capire se fare lezioni o membri internal
senza ragioni specifiche, per quanto abbia senso formarle public
o private
senza ragioni specifiche.
l'unica cosa su cui abbia mai usato la parola chiave interna è il codice di controllo della licenza nel mio prodotto ;-)
Un uso della parola chiave interna è limitare l'accesso a implementazioni concrete da parte dell'utente dell'assembly.
Se si dispone di una fabbrica o di un'altra posizione centrale per la costruzione di oggetti, l'utente dell'assembly deve occuparsi solo dell'interfaccia pubblica o della classe base astratta.
Inoltre, i costruttori interni consentono di controllare dove e quando viene istanziata una classe altrimenti pubblica.
Che ne dici di questo: in genere si consiglia di non esporre un oggetto List agli utenti esterni di un assembly, piuttosto di esporre un IEnumerable. Ma è molto più semplice usare un oggetto List all'interno dell'assembly, perché si ottiene la sintassi dell'array e tutti gli altri metodi List. Quindi, in genere ho una proprietà interna che espone un elenco da utilizzare all'interno dell'assembly.
I commenti sono ben accetti su questo approccio.
Tieni presente che qualsiasi classe definita come public
apparirà automaticamente nell'intellisense quando qualcuno guarda il tuo spazio dei nomi del progetto. Dal punto di vista dell'API, è importante mostrare agli utenti del progetto solo le classi che possono utilizzare. Usa la internal
parola chiave per nascondere cose che non dovrebbero vedere.
Se il tuo Big_Important_Class
progetto A è destinato all'uso al di fuori del tuo progetto, non dovresti contrassegnarlo internal
.
Tuttavia, in molti progetti, avrai spesso classi che sono destinate esclusivamente all'uso all'interno di un progetto. Ad esempio, potresti avere una classe che contiene gli argomenti in una chiamata al thread con parametri. In questi casi, dovresti contrassegnarli come internal
se non altro per proteggerti da una modifica API indesiderata lungo la strada.
private
parola chiave può essere utilizzata solo su classi o strutture definite all'interno di un'altra classe o struttura. Una classe definita all'interno di uno spazio dei nomi può solo essere dichiarata public
o internal
.
L'idea è che quando si progetta una libreria solo le classi che sono destinate all'uso dall'esterno (dai client della propria libreria) dovrebbero essere pubbliche. In questo modo puoi nascondere le lezioni
eccetera.
Se stai sviluppando soluzioni interne rispetto all'utilizzo di elementi interni non è così importante, immagino, perché di solito i clienti avranno un contatto costante con te e / o l'accesso al codice. Tuttavia sono abbastanza critici per gli sviluppatori di biblioteche.
Quando hai classi o metodi che non si adattano perfettamente al paradigma orientato agli oggetti, che fanno cose pericolose, che devono essere chiamate da altre classi e metodi sotto il tuo controllo e che non vuoi far usare a nessun altro .
public class DangerousClass {
public void SafeMethod() { }
internal void UpdateGlobalStateInSomeBizarreWay() { }
}