Domanda

Potresti spiegare qual è l'utilizzo pratico della parola chiave internal in C #?

So che il modificatore interno limita l'accesso all'assembly corrente, ma quando e in quale circostanza dovrei usarlo?

È stato utile?

Soluzione

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 sia accessibile.

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.

Puoi anche utilizzare il modificatore interno insieme a InternalsVisibleTo attributo a livello di assieme per creare " amico " assiemi a cui è concesso un accesso speciale alle classi interne dell'assembly di destinazione.

Questo può essere utile per la creazione di assiemi di unit testing che possono quindi chiamare i membri interni dell'assembly da testare. Ovviamente a nessun altro assembly viene concesso questo livello di accesso, quindi quando si rilascia il sistema, l'incapsulamento viene mantenuto.

Altri suggerimenti

Se Bob ha bisogno di BigImportantClass, allora Bob deve ottenere le persone che possiedono il progetto A per iscriversi 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 verrà messo in atto un processo per garantire che non venga mai modificato 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 in 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, allora & # 8220; internal & # 8221; viene utilizzato sui membri della classe che non devono essere esposti pubblicamente.

Nascondere la complessità (incapsulamento ak.a.) è 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 solo al proprio wrapper e non all'API originale, quindi non posso scherzare con niente. 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 usa questo approccio.

Essere guidato da " usa il più stretto modificatore che puoi " regola che uso interna ovunque devo accedere, per esempio, al metodo da un'altra classe fino a quando non ho esplicitamente bisogno di accedervi da un altro assembly.

Dato che l'interfaccia di assemblaggio è generalmente più ristretta 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.

Il motivo 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 che 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 servono solo per la protezione da / verso classi esterne, ma per tutte le classi.

Un altro problema è che molti sviluppatori pensano che potrebbero aver bisogno di 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 parte di questo può essere soggettivo e non sto dicendo che non dovrebbe mai essere usato. Utilizzare solo 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 altro assembly, 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.

namespace Base.Assembly
{
  public abstract class Parent
  {
    internal abstract void SomeMethod();
  }

  //This works just fine since it's in the same assembly.
  public class ChildWithin : Parent
  {
    internal override void SomeMethod()
    {
    }
  }
}

namespace Another.Assembly
{
  //Kaboom, because you can't override an internal method
  public class ChildOutside : Parent
  {
  }

  public class Test 
  { 

    //Just fine
    private Parent _parent;

    public Test()
    {
      //Still fine
      _parent = new ChildWithin();
    }
  }
}

Come puoi vedere, consente effettivamente a qualcuno di utilizzare la classe Parent senza essere in grado di ereditare da.

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, usa gli stessi file che hai usato nell'esempio 1 e modifica il livello di accessibilità di BaseClass in pubblico . Cambia anche il livello di accessibilità del membro IntM in interno . In questo caso, puoi creare un'istanza della classe, ma non puoi 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

Un uso molto interessante di internal - con il membro interno ovviamente limitato solo all'assemblea in cui è dichiarato - sta ottenendo "amico". funzionalità in una certa misura da esso. Un membro amico è qualcosa che è visibile solo a certe altre assemblee esterne all'assemblea in cui è stata dichiarata. C # non ha supporto incorporato per gli amici, tuttavia il CLR lo fa.

Puoi utilizzare InternalsVisibleToAttribute per dichiarare un'assemblea di amici e tutti i riferimenti all'interno dell'assemblea di amici tratteranno i membri interni dell'assemblea di dichiarazione come pubblici nell'ambito dell'assemblea di amici. 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.

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.

Come regola empirica ci sono due tipi di membri:

  • superficie pubblica : visibile da un'assemblea esterna (pubblica, protetta e protetta interna): il chiamante non è affidabile, pertanto è necessaria la convalida dei parametri, la documentazione del metodo, ecc.
  • superficie privata : non visibile da un assembly esterno (classi private e interne o interne): il chiamante è generalmente attendibile, pertanto è possibile omettere la convalida dei parametri, la documentazione del metodo, ecc.

Riduzione del rumore, meno tipi esponi, più semplice è la tua libreria. A prova di manomissione / Sicurezza è un altro (anche se Reflection può vincere contro di esso).

Le classi interne consentono di limitare l'API dell'assembly. Ciò 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 modifica di rottura. Senza le classi interne, dovresti presumere che cambiare i membri pubblici di qualsiasi classe sarebbe un cambiamento decisivo. Con le classi interne, puoi presumere 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;
}

Che 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 molto internal , e si sta dimostrando piuttosto utile.

Ci sono casi in cui ha senso rendere i membri delle classi interni . 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. Puoi rendere il costruttore interno , in modo che la factory (che risiede nello stesso assembly) possa creare istanze della classe, ma il codice al di fuori di quell'assembly non può.

Tuttavia, non riesco a capire se rendere le classi o i membri interni senza motivi specifici, tanto quanto abbia senso renderli pubblici o < codice> privato senza motivi specifici.

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 pubblica altrimenti.

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 verrà automaticamente visualizzata nell'intellisense quando qualcuno guarderà 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 parola chiave internal per nascondere cose che non dovrebbero vedere.

Se il tuo Big_Important_Class per il Progetto A è destinato all'uso al di fuori del tuo progetto, non dovresti contrassegnarlo internal .

Tuttavia, in molti progetti, avrai spesso classi che sono realmente 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 non intenzionale dell'API lungo la strada.

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 classi che

  1. Probabilmente cambieranno nelle versioni future (se fossero pubbliche, romperai il codice client)
  2. Sono inutili per il cliente e possono causare confusione
  3. Non sono sicuri (quindi un uso improprio potrebbe danneggiare la tua libreria piuttosto male)

ecc.

Se stai sviluppando soluzioni interne rispetto all'uso di elementi interni non è poi così importante, immagino, perché di solito i clienti avranno un contatto costante con te e / o accesso al codice. Sono piuttosto 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 lasciare chiunque altro usa.

public class DangerousClass {
    public void SafeMethod() { }
    internal void UpdateGlobalStateInSomeBizarreWay() { }
}
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top