Question

Pouvez-vous expliquer en quoi consiste l'utilisation pratique du mot clé interne en C #?

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

Était-ce utile?

La solution

Classes / méthodes utilitaires ou utilitaires auxquelles vous souhaitez accéder à partir de nombreuses autres classes du même assemblage, mais que vous souhaitez vous assurer que le code des autres assemblys ne peut pas y accéder.

De MSDN (via archive.org):

  

Le développement basé sur les composants est l’usage courant de l’accès interne, car il permet à un groupe de composants de coopérer de manière privée sans être exposé au reste du code de l’application. Par exemple, un cadre permettant de créer des interfaces utilisateur graphiques pourrait fournir des classes de contrôle et de formulaire coopérant à l'aide de membres disposant d'un accès interne. Comme ces membres sont internes, ils ne sont pas exposés au code utilisant le framework.

Vous pouvez également utiliser le modificateur internal avec le InternalsVisibleTo attribut de niveau d'assemblage pour créer " ami " assemblys disposant d'un accès spécial aux classes internes de l'assembly cible.

Cela peut être utile pour créer des assemblys de test unitaire qui sont ensuite autorisés à appeler des membres internes de l'assemblage à tester. Bien entendu, aucun autre assemblage ne bénéficie de ce niveau d'accès. Par conséquent, lorsque vous libérez votre système, l'encapsulation est conservée.

Autres conseils

Si Bob a besoin de BigImportantClass, il doit faire en sorte que les détenteurs du 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, est documenté comme répondant à ses besoins, et qu'un processus sera mis en place pour s'assurer qu'il ne sera 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 le budget pour le projet A, qu'elle peut dépenser pour autre chose.

Le problème de l’interne n’est pas que cela rende la vie difficile à Bob. Cela vous permet de contrôler les promesses coûteuses que le projet A fait concernant les fonctionnalités, la durée de vie, la compatibilité, etc.

Une autre raison d'utiliser interne est que vous obscurcissez vos fichiers binaires. L'obfuscateur sait qu'il est prudent de brouiller le nom de classe de toute classe interne, alors que le nom de classe publique ne peut pas être brouillé, car cela pourrait casser les références existantes.

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

Cacher la complexité (encapsulation ak) est le concept principal du génie logiciel de qualité.

Le mot-clé internal est très 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 souhaitez importer avec DllImport, vous pouvez importer ces fonctions en tant que fonctions statiques d'une classe et les rendre internes, afin que votre utilisateur n'ait accès qu'à votre wrapper et non à l'API d'origine. ne peut pas jouer avec rien. Les fonctions étant statiques, vous pouvez les utiliser partout dans l’assemblage, pour les multiples classes de wrapper dont vous avez besoin.

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

Etre piloté par " utiliser comme modificateur strict comme vous pouvez " rule J'utilise internal partout où j'ai besoin d'accéder, par exemple, à une méthode depuis une autre classe, jusqu'à ce que j'aie explicitement besoin d'y accéder depuis un autre assemblage.

Etant donné que l'interface d'assemblage est généralement plus étroite que la somme de ses interfaces de classes, je l'utilise assez souvent.

Je trouve interne très surutilisé. vous ne devriez vraiment pas exposer certaines fonctions à certaines catégories de personnes, mais pas à d’autres consommateurs.

Ceci à mon avis casse l'interface, casse 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 de manière différente, si possible. Cependant, cela n’est peut-être pas toujours possible.

Cela peut être causé par le fait qu'un autre développeur peut être chargé de construire une autre classe dans le même assemblage que le vôtre. Avoir des internes amoindrit la clarté de l'abstraction et peut causer des problèmes s'il est mal utilisé. 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 reste un consommateur, comme toute classe externe. L'abstraction et l'encapsulation de classes ne servent pas uniquement à protéger les classes externes, mais à toutes les classes.

Un autre problème est que beaucoup de développeurs vont penser qu’ils peuvent avoir besoin de l’utiliser ailleurs dans l’assemblage et de 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 que c'est là pour la prise. Généralement, vous souhaitez marquer privé jusqu'à ce que vous ayez un besoin impératif.

Mais une partie de cela peut être subjective, et je ne dis pas que cela ne devrait jamais être utilisé. Il suffit d’utiliser en cas de besoin.

Vous en avez vu un 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.

Supposons que vous souhaitiez qu'une classe abstraite soit vue par un autre assemblage, mais que vous ne souhaitiez pas que quelqu'un puisse en hériter. Sealed ne fonctionnera pas car c'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 assemblage.

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();
    }
  }
}

Comme vous pouvez le constater, 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’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 dans l'exemple 1 et définissez le niveau d'accessibilité de BaseClass sur public . Définissez également le niveau d'accessibilité du membre IntM sur 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

Une utilisation très intéressante d’internal - avec un membre interne se limitant bien entendu à l’assemblée dans laquelle il est déclaré - devient "ami". fonctionnalité dans une certaine mesure hors de celui-ci. 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 est déclaré. C # n’a pas de support intégré pour un ami, contrairement au CLR.

Vous pouvez utiliser InternalsVisibleToAttribute à déclarez une assemblée amie, et toutes les références de cette assemblée traiteront les membres internes de votre assemblée déclarante comme publics dans le cadre de cette assemblée. Un problème avec ceci est que tous les membres internes sont visibles; vous ne pouvez pas choisir et choisir.

InternalsVisibleTo peut être utilisé à bon escient pour exposer divers membres internes à un ensemble de tests unitaires, éliminant ainsi le besoin de solutions de réflexion complexes pour tester ces membres. Le fait que tous les membres internes soient visibles ne pose pas vraiment de problème. Cependant, cette approche gâche lourdement les interfaces de votre classe et peut potentiellement ruiner l’encapsulation au sein de l’assemblée déclarante.

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

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

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

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

Réduction du bruit, moins vous exposez de types, plus votre bibliothèque est simple. L'inviolabilité / la sécurité en est un autre (bien que Reflection puisse gagner contre elle).

Les classes internes vous permettent de limiter l’API de votre assembly. Cela présente des avantages, tels que la simplification de la compréhension de votre API.

En outre, si un bogue existe dans votre assemblage, il est moins probable que le correctif introduit un changement radical. Sans classes internes, vous devriez supposer que changer les membres du public d'une classe serait un changement décisif. 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.

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 réside dans 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'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 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 laquelle des classes d’entreprises de la bibliothèque, mais l’interface utilisateur n’a aucun moyen d’accéder directement au modèle de données, ce qui garantit que la couche d’entreprise agit toujours comme intermédiaire.

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

Il est parfois judicieux de faire des membres des classes internal . Un exemple pourrait être si vous souhaitez contrôler la manière dont les classes sont instanciées; Supposons que vous fournissiez une sorte d’usine pour créer des instances de la classe. Vous pouvez rendre le constructeur internal , de sorte que la fabrique (qui réside dans le même assemblage) puisse créer des instances de la classe, mais pas le code situé en dehors de cet assemblage.

Cependant, je ne vois aucun intérêt à rendre des classes ou des membres internes sans raisons spécifiques, aussi peu qu'il soit logique de les rendre public , ou < code> privé sans motif spécifique.

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

Une des utilisations du mot-clé internal est de limiter l'accès à des implémentations concrètes de l'utilisateur de votre assembly.

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 traiter avec 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 objet List aux utilisateurs externes d'un assemblage, mais d'exposer un IEnumerable. Mais il est beaucoup plus facile d'utiliser un objet List dans l'assembly, car vous obtenez la syntaxe de tableau et toutes les autres méthodes List. J'ai donc généralement une propriété interne qui expose une liste à utiliser dans l'assemblage.

Les commentaires sur cette approche sont les bienvenus.

N'oubliez pas que toute classe définie comme public sera automatiquement affichée dans l'intellisense lorsque quelqu'un regardera l'espace de noms de votre projet. Du point de vue de l'API, il est important d'indiquer uniquement aux utilisateurs de votre projet 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 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 réellement destinées qu'à être utilisées dans un projet. Par exemple, vous pouvez avoir une classe qui contient les arguments d'une invocation de thread paramétrée. Dans ce cas, vous devez les marquer comme internal si vous ne voulez vous protéger que contre les modifications inattendues des API.

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 qui

  1. Sont susceptibles de changer dans les versions ultérieures (si elles étaient publiques, le code client serait cassé)
  2. Sont inutiles pour le client et peuvent créer une confusion
  3. Ne sont pas en sécurité (une utilisation non conforme risquerait donc de casser votre bibliothèque)

etc.

Si vous développez des solutions internes, utiliser des éléments internes n’est pas très important, car les clients resteront en contact permanent avec vous et / ou auront accès au code. Cependant, ils sont assez critiques pour les développeurs de bibliothèques.

Lorsque vous avez des classes ou des méthodes qui ne s'inscrivent pas parfaitement 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 pas laisser quelqu'un d'autre utilise.

public class DangerousClass {
    public void SafeMethod() { }
    internal void UpdateGlobalStateInSomeBizarreWay() { }
}
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top