Utilisations pratiques du mot clé “internal” en C #

Pouvez-vous nous expliquer quelles sont les utilisations pratiques du mot-clé internal en C #?

Je sais que le modificateur internal limite l’access à l’assemblage en cours, mais quand pourrais-je en avoir besoin?

Classes / méthodes d’utilitaire ou d’aide auxquelles vous souhaitez accéder depuis de nombreuses autres classes du même assembly, mais vous voulez vous assurer que le code des autres assemblys ne peut pas y accéder.

De MSDN :

Une utilisation courante de l’access interne est 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 rest du code de l’application. Par exemple, une structure permettant de créer des interfaces utilisateur graphiques pourrait fournir des classes de contrôle et de formulaire qui coopèrent avec des membres avec un access 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 internal avec l’atsortingbut de niveau d’assemblage InternalsVisibleTo pour créer des assemblys “friend” bénéficiant d’un access spécial aux classes internes de l’assembly cible.

Cela peut être utile pour la création d’assemblages de tests unitaires qui sont ensuite autorisés à appeler les membres internes de l’assemblage à tester. Bien entendu, aucun autre assemblage ne bénéficie de ce niveau d’access. Ainsi, lorsque vous libérez votre système, l’encapsulation est maintenue.

Si Bob a besoin de BigImportantClass, Bob doit faire en sorte que les personnes qui possèdent le projet A s’inscrivent pour garantir que BigImportantClass sera écrit pour répondre à ses besoins, testé pour s’assurer qu’il répond à ses besoins et qu’un processus sera mis en place pour veiller à ce qu’il ne soit jamais modifié pour ne plus répondre à ses besoins.

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

Le sharepoint vue interne n’est pas que cela rend la vie difficile à Bob. C’est ce qui vous permet de contrôler les promesses coûteuses du projet A sur les fonctionnalités, la durée de vie, la compatibilité, etc.

Une autre raison d’utiliser interne est si vous obscurcissez vos fichiers binarys. L’obfuscateur sait qu’il est sécuritaire de brouiller le nom de classe de toutes les classes internes, alors que le nom des classes publiques ne peut pas être brouillé, car cela pourrait briser les références existantes.

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

Hiding complexité (aka encapsulation) est le concept principal de l’ingénierie logicielle de qualité.

Le mot clé internal est fortement utilisé lorsque vous créez un wrapper sur du code non géré.

Lorsque vous avez une bibliothèque basée sur C / C ++ que vous voulez DllImport, vous pouvez importer ces fonctions en tant que fonctions statiques d’une classe et les rendre internes afin que votre utilisateur n’ait access qu’à votre wrapper et non à l’API d’origine. jouer avec n’importe quoi. Les fonctions étant statiques, vous pouvez les utiliser partout dans l’assemblage, pour les différentes classes d’encapsuleur dont vous avez besoin.

Vous pouvez jeter un oeil à Mono.Cairo, c’est une enveloppe autour de la bibliothèque cairo qui utilise cette approche.

Etre piloté par “utiliser un modificateur aussi ssortingct que possible” règle que j’utilise internal partout où j’ai besoin d’accéder, disons, à une méthode d’une autre classe jusqu’à ce que j’aie explicitement besoin d’y accéder depuis un autre assembly.

Comme l’interface d’assemblage est généralement plus étroite que la sum de ses interfaces de classes, il y a beaucoup d’endroits où je l’utilise.

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

Ceci à mon avis casse l’interface, brise l’abstraction. Cela ne veut pas dire qu’il ne devrait jamais être utilisé, mais une meilleure solution consiste à réorganiser une classe différente ou à l’utiliser si possible. Cependant, cela peut ne pas être toujours possible.

Les raisons pour lesquelles cela peut causer des problèmes sont qu’un autre développeur peut être chargé de créer une autre classe dans le même assemblage que le vôtre. Avoir des internes diminue la clarté de l’abstraction et peut causer des problèmes si elle est mal utilisée. Ce serait le même problème que si vous l’aviez rendu public. L’autre classe en cours de construction par l’autre développeur rest un consommateur, comme toute classe externe. L’abstraction et l’encapsulation de classes ne servent pas uniquement à la protection des classes externes, mais pour toutes les classes.

Un autre problème est que beaucoup de développeurs pensent qu’ils peuvent avoir besoin de l’utiliser ailleurs dans l’assemblage et de le marquer comme étant interne, même s’ils n’en ont pas besoin à l’époque. Un autre développeur peut alors penser que c’est là pour le prendre. En règle générale, vous voulez marquer privé jusqu’à ce que vous ayez un besoin définitif.

Mais certains peuvent être subjectifs, et je ne dis pas que cela ne devrait jamais être utilisé. Utilisez juste quand vous en avez besoin.

J’ai vu un autre jour intéressant, peut-être une semaine, sur un blog dont je ne me souviens plus. Fondamentalement, je ne peux pas prendre le crédit pour cela, mais je pensais que cela pourrait avoir une application utile.

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

 espace de noms Base.Assembly
 {
   classe publique abstraite Parent
   {
     abstract interne void SomeMethod ();
   }

   // Cela fonctionne très bien car c'est dans le même assemblage.
   Classe publique ChildWithin: Parent
   {
     remplacement interne void SomeMethod ()
     {
     }
   }
 }

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

   test de classe publique 
   { 

     //Ça va
     parent privé _parent;

     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.

Cet exemple contient deux fichiers: Assembly1.cs et Assembly2.cs. Le premier fichier contient une classe de base interne, BaseClass. Dans le second fichier, une tentative d’instancier BaseClass produira une erreur.

 // Assembly1.cs // comstack with: /target:library internal class BaseClass { public static int intM = 0; } // Assembly1_a.cs // comstack with: /reference:Assembly1.dll class TestAccess { static void Main() { BaseClass myBase = new BaseClass(); // CS0122 } } 

Dans cet exemple, utilisez les mêmes fichiers que ceux utilisés dans l’exemple 1 et modifiez le niveau d’accessibilité de BaseClass à 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 // comstack with: /target:library public class BaseClass { internal static int intM = 0; } // Assembly2_a.cs // comstack 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

Une utilisation très intéressante de l’interne – dont le membre interne est bien sûr limité à l’assemblée dans laquelle il est déclaré – permet d’obtenir un certain degré de fonctionnalité «ami». Un membre ami est quelque chose qui est visible uniquement à certaines autres assemblées en dehors de l’assembly dans lequel il est déclaré. C # n’a pas de support intégré pour l’ami, mais le CLR le fait.

Vous pouvez utiliser InternalsVisibleToAtsortingbute pour déclarer un assembly ami et toutes les références de l’assembly friend traiteront les membres internes de votre assembly déclarant comme étant publics dans la scope de l’assembly friend. Un problème avec ceci est que tous les membres internes sont visibles; vous ne pouvez pas choisir.

Une bonne utilisation d’InternalsVisibleTo consiste à exposer divers membres internes à un ensemble de tests unitaires, éliminant ainsi le besoin de contourner des parsings complexes pour tester ces membres. Tous les membres internes visibles ne sont pas un problème, cependant, cette approche réduit considérablement les interfaces de votre classe et peut potentiellement détruire l’encapsulation au sein de l’assembly déclarant.

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

Par exemple, une DAL peut avoir un ORM, mais les objects ne doivent pas être exposés à la couche de gestion. Toute interaction doit être effectuée via des méthodes statiques et en transmettant les parameters requirejs.

La réduction du bruit, moins vous exposez de types, plus votre bibliothèque est simple. L’étanchéité à la fraude / sécurité en est une autre (même si Reflection peut gagner contre).

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

  • surface publique : visible depuis un assembly externe (public, protégé et protégé en interne): l’appelant n’est pas fiable, donc la validation des parameters, la documentation de la méthode, etc. sont nécessaires.
  • surface privée : non visible depuis un assembly externe (privé et interne, ou classes internes): l’appelant est généralement approuvé, de sorte que la validation des parameters, la documentation de la méthode, etc. peuvent être omises.

Les classes internes vous permettent de limiter l’API de votre assembly. Cela présente des avantages, comme rendre votre API plus facile à 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 devriez supposer que changer les membres publics d’une classe serait un changement radical. Avec les classes internes, vous pouvez supposer que la modification de leurs membres publics interrompt uniquement l’API interne de l’assembly (et tout assemblage référencé dans l’atsortingbut InternalsVisibleTo).

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

J’ai un projet qui utilise LINQ-to-SQL pour le back-end des données. J’ai deux espaces de noms principaux: Biz et Data. Le modèle de données LINQ vit dans les données et est marqué “interne”; l’espace de noms Biz a des classes publiques qui entourent 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’object de données, par exemple:

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

Les objects Biz ont un constructeur privé (pour forcer l’utilisation de 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 l’interface utilisateur n’a aucun moyen d’accéder directement au modèle de données, garantissant ainsi que la couche de gestion agit toujours comme un intermédiaire.

C’est la première fois que j’utilise vraiment beaucoup d’ internal , et cela s’avère très utile.

Il y a des cas où il est logique de rendre les membres des classes internal . Un exemple pourrait être si vous voulez contrôler la manière dont les classes sont instanciées; Disons que vous fournissez une sorte de fabrique pour créer des instances de la classe. Vous pouvez rendre le constructeur internal pour 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 à rendre les classes ou les membres internal sans raisons spécifiques, aussi peu qu’il soit logique de les rendre public ou private sans raisons spécifiques.

la seule chose que j’ai jamais utilisée est le code de vérification de licence de mon produit 😉

Une des utilisations du mot-clé interne est de limiter l’access aux implémentations concrètes de l’utilisateur de votre assembly.

Si vous avez une fabrique ou un autre emplacement central pour la construction d’objects, l’utilisateur de votre assembly ne doit gérer que l’interface publique ou la classe de base abstraite.

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

Qu’en est-il de celui-ci: il est généralement recommandé de ne pas exposer un object List aux utilisateurs externes d’un assemblage, mais plutôt d’exposer un IEnumerable. Mais il est beaucoup plus facile d’utiliser un object List dans 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 dans l’assemblage.

Les commentaires sur cette approche sont les bienvenus.

N’oubliez pas que toute classe définie comme public apparaîtra automatiquement dans intellisense lorsque quelqu’un examine l’espace de noms de votre projet. Du sharepoint vue de l’API, il est important de ne montrer aux utilisateurs de votre projet que les classes qu’ils peuvent utiliser. Utilisez le mot-clé internal pour masquer les éléments qu’ils ne doivent pas voir.

Si votre Big_Important_Class pour Project A est destiné à être utilisé en dehors de votre projet, vous ne devez pas le marquer comme étant internal .

Cependant, dans de nombreux projets, vous avez souvent des classes qui ne sont vraiment destinées qu’à être utilisées dans un projet. Par exemple, vous pouvez avoir une classe contenant les arguments à une invocation de thread paramétrée. Dans ces cas, vous devez les marquer comme internal si ce n’est que pour vous protéger d’un changement d’API involontaire.

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 (s’ils étaient publics, vous casseriez le code du client)
  2. Sont inutiles pour le client et peuvent causer de la confusion
  3. Ne sont pas en sécurité (une utilisation inappropriée pourrait donc briser votre bibliothèque)

etc.

Si vous développez des solutions internes plutôt que d’utiliser des éléments internes, ce n’est pas très important, car les clients sont en contact permanent avec vous et / ou ont access au code. Ils sont cependant assez critiques pour les développeurs de bibliothèques.

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

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