Utilisations pratiques du mot-clé «interne» en C #


420

Pourriez-vous expliquer à quoi sert le internalmot clé en C #?

Je sais que le internalmodificateur limite l'accès à l'assembly actuel, mais quand et dans quelles circonstances dois-je l'utiliser?

Réponses:


379

Classes / méthodes utilitaires ou auxiliaires auxquelles vous souhaitez accéder à partir de nombreuses autres classes au sein du même assembly, mais auxquelles vous voulez vous assurer que le code dans d'autres assemblys ne peut pas accéder.

Depuis MSDN (via archive.org):

L'accès interne est couramment utilisé dans le développement basé sur les composants, car il permet à un groupe de composants de coopérer de manière privée sans être exposé au reste du code d'application. Par exemple, un cadre pour la construction d'interfaces utilisateur graphiques pourrait fournir des classes de contrôle et de formulaire qui coopèrent en utilisant des membres avec un accès interne. Étant donné que ces membres sont internes, ils ne sont pas exposés au code qui utilise le framework.

Vous pouvez également utiliser le modificateur interne ainsi que l' InternalsVisibleToattribut de niveau d'assemblage pour créer des assemblys "amis" qui bénéficient d'un accès spécial aux classes internes d'assembly cible.

Cela peut être utile pour la création d'assemblys de test unitaire qui sont ensuite autorisés à appeler des membres internes de l'assembly à tester. Bien sûr, aucun autre assemblage ne bénéficie de ce niveau d'accès, donc lorsque vous libérez votre système, l'encapsulation est maintenue.


8
L'attribut InternalsVisibleTo est d'une grande aide. Je vous remercie.
érection

33
Mon sentiment est qu'à partir du moment où vous avez besoin interne, il y a quelque chose de mal quelque part dans la conception. À mon humble avis, on devrait pouvoir utiliser de l'OO pur pour résoudre tous les problèmes. En ce moment même, je regarde environ le millionième usage de interne dans la source .NET Framework, m'interdisant de tirer parti de ce qu'ils utilisent eux-mêmes. Grâce à l'utilisation de internes, ils ont créé un monolithe qui est superficiellement modulaire, mais se décompose lorsque vous essayez d'isoler des choses. De plus, la sécurité n'est pas un argument, car il y a une réflexion à la rescousse.
Grimace of Despair

27
@GrimaceofDespair: Permettez-moi d'ajouter un commentaire de désaccord ici. Personnellement, je considère interne comme une version très utile du modificateur "public" dans les grandes solutions multi-projets. L'ajout de ce modificateur à une classe de mon sous-projet interdit "l'utilisation aléatoire" de cette classe par d'autres sous-projets de la solution et me permet de forcer l'utilisation appropriée de ma bibliothèque. Si j'ai besoin de refactoriser plus tard, je n'aurai pas à me demander "attendez, mais pourquoi ce type est-il allé créer une instance de la classe Point dont je n'avais besoin qu'à mes propres fins dans ce calcul particulier"?
KT.

4
Pour le dire autrement, si tout le monde dépendait des composants internes de SmtpClient, et à un moment où un bogue y était découvert, .NET aurait de sérieux problèmes pour corriger ce bogue, peut-être même le garder longtemps. Je me souviens avoir lu un article amusant une fois qui décrivait la longueur à laquelle les développeurs Windows devaient aller pour conserver la «compatibilité avec les bogues» avec tous les anciens programmes qui utilisaient diverses fonctionnalités internes et bogues. Répéter cette erreur avec .NET n'a pas de sens.
KT.

13
Je pense que interne est bien plus utile que public. La seule fois où vous utilisez public est lorsque vous dites explicitement "Ici, utilisateur - je vous propose une fonction utile à utiliser." Si vous écrivez une application plutôt qu'une bibliothèque utilisateur, tout doit être interne car vous n'essayez pas de donner aux gens des fonctions à appeler en externe. Si vous écrivez une bibliothèque utilisateur, seules les fonctions utiles que vous vous attendez à donner à l'utilisateur, à maintenir, à prendre en charge et à conserver jusqu'à la fin des temps devraient être publiques. Sinon, rendez-le interne.
Darrell Plank

85

Si Bob a besoin de BigImportantClass, alors Bob doit obliger les personnes qui possèdent le projet A à s'inscrire pour garantir que BigImportantClass sera écrit pour répondre à ses besoins, testé pour s'assurer qu'il répond à ses besoins, est documenté comme répondant à ses besoins et qu'un processus sera mis en place pour qu'il ne soit jamais modifié afin de ne plus répondre à ses besoins.

Si une classe est interne, elle n'a pas à passer par ce processus, ce qui économise du budget pour le projet A qu'elle peut dépenser pour d'autres choses.

L'intérêt n'est pas que cela rend la vie difficile à Bob. C'est qu'il vous permet de contrôler les promesses coûteuses du projet A concernant les fonctionnalités, la durée de vie, la compatibilité, etc.


5
C'est parfaitement logique. Je souhaite juste que nous ayons eu la possibilité de remplacer les membres internes, car nous sommes tous des adultes consentants ici.
cwallenpoole

6
@EricLippert Pas tout à fait ce que je voulais dire. Si j'hérite d'un code compilé contenant "interne", je suis bloqué, non? Il n'y a aucun moyen de dire simplement au compilateur "Non, cet autre développeur vous a menti. Vous devriez plutôt me faire confiance" à moins que j'utilise la réflexion.
cwallenpoole

11
@cwallenpoole: Si vous utilisez du code écrit par des menteurs qui écrivent du code que vous n'aimez pas, arrêtez d'utiliser leur code et écrivez votre propre code que vous préférez.
Eric Lippert

2
@EricLippert C'était censé être ironique, je ne voulais pas offenser. (J'essayais surtout de confirmer que je suis SOL dans de tels cas).
cwallenpoole

5
@cwallenpoole: Je ne suis pas du tout offensé. J'offre de bons conseils si vous vous retrouvez coincé dans cette situation malheureuse.
Eric Lippert

60

Une autre raison d'utiliser interne est que vous obscurcissez vos fichiers binaires. L'obfuscateur sait qu'il est sûr de brouiller le nom de classe des classes internes, tandis que le nom des classes publiques ne peut pas être brouillé, car cela pourrait casser les références existantes.


5
Pff. En regardant le bytecode avec un désassembleur, il sera toujours assez clair de toute façon ce que fait le code. Les obscurcisseurs sont à peine un moyen de dissuasion léger pour quiconque essaie vraiment d'entrer dans les internes. Je n'ai jamais entendu parler d'un pirate informatique qui s'est arrêté juste parce qu'il n'a obtenu aucun nom de fonction utile.
Nyerguds

29
donc tu ne verrouille pas ta porte quand tu dors, parce que tu n'as jamais entendu parler d'un voleur arrêté par une serrure?
Sergio

4
C'est pourquoi je brouille les noms de classe et de variable dans le code source. Vous pourriez être en mesure de comprendre ce qu'est un PumpStateComparator, mais pouvez-vous deviner ce qu'est un LpWj4mWE? #jobsecurity (je ne fais pas ça, et je vous en prie, ne faites pas ça, les internautes.)
Slothario

32

Si vous écrivez une DLL qui encapsule une tonne de fonctionnalités complexes dans une API publique simple, alors «interne» est utilisé sur les membres de la classe qui ne doivent pas être exposés publiquement.

Cacher la complexité (ou encapsulation) est le concept principal de l'ingénierie logicielle de qualité.


20

Le mot-clé interne est largement utilisé lorsque vous créez un wrapper sur du code non géré.

Lorsque vous disposez d'une bibliothèque basée sur C / C ++ que vous souhaitez importer dans Dll, vous pouvez importer ces fonctions en tant que fonctions statiques d'une classe et les rendre internes, de sorte que votre utilisateur n'a accès qu'à votre wrapper et non à l'API d'origine, il ne peut donc pas jouer avec quoi que ce soit. Les fonctions étant statiques, vous pouvez les utiliser partout dans l'assembly, pour les multiples classes wrapper dont vous avez besoin.

Vous pouvez jeter un œil à Mono.Cairo, c'est un wrapper autour de la bibliothèque du Caire qui utilise cette approche.


12

Étant motivé par la règle "utilisez le modificateur le plus strict possible", j'utilise interne partout où j'ai besoin d'accéder, par exemple, à une méthode d'une autre classe jusqu'à ce que j'aie explicitement besoin d'y accéder à partir d'un autre assembly.

Comme l'interface d'assemblage est généralement plus étroite que la somme de ses interfaces de classes, je l'utilise à de nombreux endroits.


3
Si vous "utilisez le modificateur le plus strict possible", pourquoi pas privé alors?
R. Martinho Fernandes

6
Étant donné que private est trop strict lorsque les propriétés / méthodes de classe doivent être accessibles en dehors de la classe, et les cas où elles doivent être accessibles à partir d'une autre asse, bly ne le sont pas si souvent.
Ilya Komakhin,

11

Je trouve interne d'être beaucoup trop utilisé. vous ne devriez vraiment pas exposer certaines fonctionnalités uniquement à certaines classes que vous ne feriez pas à d'autres consommateurs.

À mon avis, cela brise l'interface, brise l'abstraction. Cela ne veut pas dire qu'il ne devrait jamais être utilisé, mais une meilleure solution consiste à refactoriser une classe différente ou à l'utiliser d'une manière différente si possible. Cependant, cela n'est pas toujours possible.

Les raisons pour lesquelles cela peut provoquer des problèmes sont qu'un autre développeur peut être chargé de créer une autre classe dans le même assembly que le vôtre. Le fait d'avoir des internes diminue la clarté de l'abstraction et peut causer des problèmes en cas de mauvaise utilisation. Ce serait le même problème que si vous le rendiez public. L'autre classe en cours de construction par l'autre développeur est toujours un consommateur, comme toute classe externe. L'abstraction et l'encapsulation de classes ne sont pas uniquement destinées à protéger les classes externes, mais toutes les classes.

Un autre problème est que de nombreux développeurs penseront qu'ils devront peut-être l'utiliser ailleurs dans l'assemblage et le marquer comme interne de toute façon, même s'ils n'en ont pas besoin à ce moment-là. Un autre développeur peut alors penser qu'il est là pour la prise. En règle générale, vous souhaitez marquer comme privé jusqu'à ce que vous ayez un besoin définitif.

Mais certains de ces éléments peuvent être subjectifs, et je ne dis pas que cela ne devrait jamais être utilisé. Utilisez-le au besoin.


Je crée souvent des objets de domaine pour lesquels ils ont des membres qui devraient être publics pour d'autres objets de domaine au sein du même assembly. Toutefois, ces mêmes membres ne doivent PAS être disponibles pour le code client qui est en dehors de l'assembly. Pour des situations comme celle-ci, «interne» est très approprié.
Rock Anthony Johnson

8

J'en ai vu un intéressant l'autre jour, peut-être une semaine, sur un blog dont je ne me souviens pas. Fondamentalement, je ne peux pas m'en attribuer le mérite, mais j'ai pensé que cela pourrait avoir une application utile.

Supposons que vous vouliez qu'une classe abstraite soit vue par un autre assembly, mais que vous ne voulez pas que quelqu'un puisse en hériter. Sealed ne fonctionnera pas parce qu'il est abstrait pour une raison, les autres classes de cet assemblage en héritent. Private ne fonctionnera pas car vous voudrez peut-être déclarer une classe Parent quelque part dans l'autre assembly.

espace de noms Base.Assembly
{
  classe abstraite publique Parent
  {
    résumé interne vide SomeMethod ();
  }

  // Cela fonctionne très bien car il est dans le même assemblage.
  public class ChildWithin: Parent
  {
    remplacement interne void SomeMethod ()
    {
    }
  }
}

espace de noms Another.Assembly
{
  // Kaboom, car vous ne pouvez pas remplacer une méthode interne
  classe public ChildOutside: Parent
  {
  }

  test de classe publique 
  { 

    //Ça va
    Parent _parent privé;

    test public ()
    {
      // Toujours bien
      _parent = new ChildWithin ();
    }
  }
}

Comme vous pouvez le voir, cela permet effectivement à quelqu'un d'utiliser la classe Parent sans pouvoir en hériter.


7

Cet exemple contient deux fichiers: Assembly1.cs et Assembly2.cs. Le premier fichier contient une classe de base interne, BaseClass. Dans le deuxième fichier, une tentative d'instanciation de BaseClass produira une erreur.

// 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
   }
}

Dans cet exemple, utilisez les mêmes fichiers que vous avez utilisés dans l'exemple 1 et modifiez le niveau d'accessibilité de BaseClass en public . Modifiez également le niveau d'accessibilité du membre IntM en interne . Dans ce cas, vous pouvez instancier la classe, mais vous ne pouvez pas accéder au membre interne.

// 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
   }
}

source : http://msdn.microsoft.com/en-us/library/7c5ka91b(VS.80).aspx


6

Lorsque vous avez des méthodes, des classes, etc. qui doivent être accessibles dans le cadre de l'assembly actuel et jamais en dehors de celui-ci.

Par exemple, un DAL peut avoir un ORM, mais les objets ne doivent pas être exposés à la couche métier, toutes les interactions doivent être effectuées via des méthodes statiques et en transmettant les paramètres requis.


6

Une utilisation très intéressante de interne - avec un membre interne bien sûr limité uniquement à l'assembly dans lequel il est déclaré - permet d'en retirer dans une certaine mesure la fonctionnalité "ami". Un membre ami est quelque chose qui n'est visible que par certaines autres assemblées en dehors de l'assemblée dans laquelle il a été déclaré. C # n'a pas de support intégré pour les amis, contrairement au CLR.

Vous pouvez utiliser InternalsVisibleToAttribute pour déclarer un assembly friend, et toutes les références de l'assembly friend traiteront les membres internes de votre assembly déclarant comme publics dans la portée de l'assembly friend. Un problème avec cela est que tous les membres internes sont visibles; vous ne pouvez pas choisir.

Une bonne utilisation pour InternalsVisibleTo consiste à exposer divers membres internes à un ensemble de test unitaire, éliminant ainsi les besoins de travaux de réflexion complexes pour tester ces membres. La visibilité de tous les membres internes n'est pas vraiment un problème, mais l'adoption de cette approche détruit assez fortement vos interfaces de classe et peut potentiellement ruiner l'encapsulation au sein de l'assembly déclarant.


5

En règle générale, il existe deux types de membres:

  • surface publique : visible depuis un assemblage externe (public, protégé et protégé en interne): l'appelant n'est pas approuvé, donc la validation des paramètres, la documentation de la méthode, etc. est nécessaire.
  • surface privée : non visible d'un assemblage externe (classes privées et internes ou internes): l'appelant est généralement approuvé, donc la validation des paramètres, la documentation de la méthode, etc. peuvent être omises.

4

Réduction du bruit, moins vous exposez de types, plus votre bibliothèque est simple. La protection contre les falsifications / sécurité en est une autre (bien que Reflection puisse gagner contre elle).


4

Les classes internes vous permettent de limiter l'API de votre assembly. Cela présente des avantages, comme rendre votre API plus simple à comprendre.

De plus, si un bogue existe dans votre assembly, il y a moins de chance que le correctif introduise un changement de rupture. Sans classes internes, vous devez supposer que changer les membres publics d'une classe serait un changement de rupture. Avec les classes internes, vous pouvez supposer que la modification de leurs membres publics ne rompt que l'API interne de l'assembly (et tous les assemblys référencés dans l'attribut InternalsVisibleTo).

J'aime avoir l'encapsulation au niveau de la classe et au niveau de l'assemblage. Certains sont en désaccord avec cela, mais il est bon de savoir que la fonctionnalité est disponible.


2

J'ai un projet qui utilise LINQ-to-SQL pour le back-end de données. J'ai deux espaces de noms principaux: Biz et Data. Le modèle de données LINQ vit dans Data et est marqué "interne"; l'espace de noms Biz a des classes publiques qui enveloppent les classes de données LINQ.

Il y a donc Data.Client, et Biz.Client; ce dernier expose toutes les propriétés pertinentes de l'objet de données, par exemple:

private Data.Client _client;
public int Id { get { return _client.Id; } set { _client.Id = value; } }

Les objets Biz ont un constructeur privé (pour forcer l'utilisation des méthodes d'usine) et un constructeur interne qui ressemble à ceci:

internal Client(Data.Client client) {
    this._client = client;
}

Cela peut être utilisé par n'importe quelle classe métier de la bibliothèque, mais le frontal (UI) n'a aucun moyen d'accéder directement au modèle de données, garantissant que la couche métier agit toujours comme intermédiaire.

C'est la première fois que j'en utilise internalbeaucoup et cela s'avère très utile.


2

Il y a des cas où il est logique de faire des membres de classes internal. Un exemple pourrait être si vous voulez contrôler la façon dont les classes sont instanciées; disons que vous fournissez une sorte d'usine pour créer des instances de la classe. Vous pouvez créer le constructeur internal, de sorte que la fabrique (qui réside dans le même assembly) puisse créer des instances de la classe, mais pas le code en dehors de cet assembly.

Cependant, je ne vois aucun intérêt à créer des classes ou des membres internalsans raisons spécifiques, aussi peu qu'il est logique de les faire public, ou privatesans raisons spécifiques.


1

la seule chose sur laquelle j'ai jamais utilisé le mot-clé interne est le code de vérification de licence dans mon produit ;-)


1

Une utilisation du mot-clé interne est de limiter l'accès aux implémentations concrètes de l'utilisateur de votre assemblage.

Si vous avez une usine ou un autre emplacement central pour la construction d'objets, l'utilisateur de votre assemblage n'a besoin que de l'interface publique ou de la classe de base abstraite.

De plus, les constructeurs internes vous permettent de contrôler où et quand une classe par ailleurs publique est instanciée.


1

Qu'en est-il de celui-ci: il est généralement recommandé de ne pas exposer un objet List aux utilisateurs externes d'un assembly, mais plutôt d'exposer un IEnumerable. Mais il est beaucoup plus facile d'utiliser un objet List à l'intérieur de l'assembly, car vous obtenez la syntaxe du tableau et toutes les autres méthodes List. Donc, j'ai généralement une propriété interne exposant une liste à utiliser à l'intérieur de l'assembly.

Les commentaires sont les bienvenus sur cette approche.


@Samik - pourriez-vous s'il vous plaît développer, "il est généralement recommandé de ne pas exposer un objet List aux utilisateurs externes d'un assembly, plutôt d'exposer un IEnumerable." pourquoi est-il préférable d'innombrables?
Howiecamp

1
@Howiecamp: Principalement parce que IEnumerable donne un accès en lecture seule à la liste, où comme si vous exposiez la liste directement, elle peut être modifiée par les clients (comme supprimer des éléments, etc.), ce qui peut être involontaire.
Samik R

En outre, l'exposition d'une liste ou d'une liste crée un contrat que vous autoriserez toujours (par exemple) un accès aléatoire rapide aux données. Si un jour vous décidez de changer votre méthode pour utiliser une autre collection, ou pas de collection du tout (rendement retour), vous devrez soit créer une liste juste pour retourner une valeur, soit rompre le contrat en changeant le type de retour de la méthode. L'utilisation d'un type de retour moins spécifique vous donne de la flexibilité.

1

Gardez à l'esprit que toute classe définie comme publicapparaîtra automatiquement dans l'intellisense lorsque quelqu'un regarde l'espace de noms de votre projet. Du point de vue de l'API, il est important de ne montrer aux utilisateurs de votre projet que les classes qu'ils peuvent utiliser. Utilisez le internalmot - clé pour masquer les choses qu'ils ne devraient pas voir.

Si votre Big_Important_Classpour le projet A est destiné à être utilisé en dehors de votre projet, vous ne devez pas le marquer internal.

Cependant, dans de nombreux projets, vous aurez souvent des classes qui ne sont vraiment destinées qu'à être utilisées dans un projet. Par exemple, vous pouvez avoir une classe qui contient les arguments d'un appel de thread paramétré. Dans ces cas, vous devez les marquer comme internalsi pour aucune autre raison que de vous protéger contre un changement d'API involontaire en cours de route.


Alors pourquoi ne pas utiliser plutôt Private?
Prisonnier ZERO du

2
Le privatemot-clé ne peut être utilisé que sur des classes ou des structures définies dans une autre classe ou structure. Une classe définie dans un espace de noms peut uniquement être déclarée publicou internal.
Matt Davis

1

L'idée est que lorsque vous concevez une bibliothèque, seules les classes destinées à être utilisées de l'extérieur (par les clients de votre bibliothèque) doivent être publiques. De cette façon, vous pouvez masquer les classes

  1. Sont susceptibles de changer dans les versions futures (si elles étaient publiques, vous briseriez le code client)
  2. Sont inutiles pour le client et peuvent prêter à confusion
  3. Ne sont pas sûrs (une utilisation incorrecte pourrait donc endommager gravement votre bibliothèque)

etc.

Si vous développez des solutions internes, l'utilisation d'éléments internes n'est pas si importante que cela, car les clients auront généralement un contact constant avec vous et / ou un accès au code. Ils sont cependant assez critiques pour les développeurs de bibliothèques.


-7

Lorsque vous avez des classes ou des méthodes qui ne rentrent pas correctement dans le paradigme orienté objet, qui font des choses dangereuses, qui doivent être appelées à partir d'autres classes et méthodes sous votre contrôle, et que vous ne voulez laisser personne d'autre utiliser .

public class DangerousClass {
    public void SafeMethod() { }
    internal void UpdateGlobalStateInSomeBizarreWay() { }
}

Qu'est-ce que tu essayes de dire? Ce n'est pas très clair. Du moins pas pour moi.
pqsk

En accord avec @pqsk
Anynomous Khan
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.