Pourriez-vous expliquer à quoi sert le internal
mot clé en C #?
Je sais que le internal
modificateur limite l'accès à l'assembly actuel, mais quand et dans quelles circonstances dois-je l'utiliser?
Pourriez-vous expliquer à quoi sert le internal
mot clé en C #?
Je sais que le internal
modificateur limite l'accès à l'assembly actuel, mais quand et dans quelles circonstances dois-je l'utiliser?
Réponses:
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' InternalsVisibleTo
attribut 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.
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.
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.
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é.
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.
É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.
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.
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.
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
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.
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.
En règle générale, il existe deux types de membres:
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).
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.
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 internal
beaucoup et cela s'avère très utile.
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 internal
sans raisons spécifiques, aussi peu qu'il est logique de les faire public
, ou private
sans raisons spécifiques.
la seule chose sur laquelle j'ai jamais utilisé le mot-clé interne est le code de vérification de licence dans mon produit ;-)
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.
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.
Gardez à l'esprit que toute classe définie comme public
apparaî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 internal
mot - clé pour masquer les choses qu'ils ne devraient 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 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 internal
si pour aucune autre raison que de vous protéger contre un changement d'API involontaire en cours de route.
private
mot-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 public
ou internal
.
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
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.
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() { }
}