web-dev-qa-db-fra.com

Pourquoi C # ne fournit-il pas le mot clé "ami" de style C ++?

Le mot clé ami C++ autorise un class A pour désigner class B comme son ami. Ceci permet Class B pour accéder aux private/protected membres de class A.

Je n'ai jamais rien lu qui explique pourquoi cela a été laissé hors de C # (et VB.NET). La plupart des réponses à cette question précédente de StackOverflow semblent dire que c'est une partie utile de C++ et qu'il y a de bonnes raisons de l'utiliser. Dans mon expérience, je devrais être d'accord.

Une autre question me semble vraiment demander comment faire quelque chose de similaire à friend dans une application C #. Bien que les réponses tournent généralement autour des classes imbriquées, cela ne semble pas aussi élégant que d'utiliser le mot clé friend.

L'original Design Patterns book l'utilise régulièrement dans ses exemples.

Donc, en résumé, pourquoi friend est-il absent de C # et quelle est la "meilleure pratique" (ou les méthodes) de simulation en C #?

(Au fait, le mot-clé internal est pas la même chose, il permet tout les classes de l'ensemble de l'Assemblée pour accéder à internal membres, tandis que friend permet de donner à une certaine classe un accès complet à exactement une autre classe)

205
Ash

Avoir des amis dans la programmation est plus ou moins considéré comme "sale" et facile à abuser. Il rompt les relations entre les classes et sape certains attributs fondamentaux d’une langue OO.

Ceci étant dit, c'est une fonctionnalité de Nice et je l'ai déjà utilisée beaucoup de fois en C++; et voudrait l'utiliser aussi en C #. Mais je parie que, du fait de la pureté de C # sur OOness (comparé au pseudo OOness de C++), MS a décidé que Java ne possède pas de mot clé ami C # ne devrait pas non plus (je plaisante;;))

Sur une note sérieuse: interne n'est pas aussi bon qu'un ami mais il fait le travail. N'oubliez pas qu'il est rare que vous distribuiez votre code à des développeurs tiers, et non via une DLL. donc tant que vous et votre équipe connaissez les classes internes et leur utilisation, vous devriez aller bien.

EDIT Permettez-moi de préciser en quoi le mot clé friend porte atteinte à la POO.

Les variables et méthodes privées et protégées sont peut-être l’une des parties les plus importantes de la programmation orientée objet. L'idée que les objets peuvent contenir des données ou une logique qu'ils peuvent utiliser uniquement vous permet d'écrire votre implémentation de fonctionnalités indépendamment de votre environnement - et que votre environnement ne peut pas modifier les informations d'état qu'il n'est pas adapté pour gérer. En utilisant friend, vous couplez les implémentations de deux classes, ce qui est bien pire si vous ne faites que coupler leur interface.

64
user19302

Sur une note de côté. Utiliser Friend ne consiste pas à violer l’encapsulation, mais au contraire à l’appliquer. Comme les accesseurs + mutateurs, la surcharge des opérateurs, l'héritage public, le downcasting, , etc. , il est souvent mal utilisé, mais cela ne veut pas dire que le mot clé n'a pas, ou pire , un mauvais but.

Voir Konrad Rudolphmessage dans l'autre fil, ou si vous préférez voir l'entrée correspondante dans la FAQ C++.

100
Luc Hermitte

Pour info, une autre chose liée mais pas tout à fait identique dans .NET est [InternalsVisibleTo], qui permet à une assemblée de désigner une autre assemblée (telle qu'une assemblée de test unitaire) disposant (effectivement) d'un accès "interne" aux types/membres de l'assemblage d'origine.

47
Marc Gravell

Avec friend, un concepteur C++ a un contrôle précis sur les personnes auxquelles les membres privés * sont exposés. Mais, il est obligé d'exposer chacun des membres privés.

Avec internal, un concepteur C # a un contrôle précis sur l’ensemble des membres privés qu’il expose. De toute évidence, il ne peut exposer qu’un seul membre privé. Mais, il sera exposé à toutes les classes de l'Assemblée.

Généralement, un concepteur ne souhaite exposer que quelques méthodes privées à quelques autres classes. Par exemple, dans un modèle de fabrique de classes, il peut être souhaitable que la classe C1 soit instanciée uniquement par la fabrique de classes CF1. Par conséquent, la classe C1 peut avoir un constructeur protégé et une fabrique de classe d'amis CF1.

Comme vous pouvez le constater, nous avons 2 dimensions sur lesquelles l’encapsulation peut être violée. friend la franchit dans une dimension, internal dans l'autre. Laquelle est la pire violation du concept d’encapsulation? Dur à dire. Mais il serait bien d’avoir à la fois friend et internal disponibles. En outre, un bon ajout à ces deux types serait le troisième type de mot-clé, qui serait utilisé membre par membre (comme internal) et spécifiant la classe cible (comme friend). .

* Par souci de brièveté, je vais utiliser "privé" au lieu de "privé et/ou protégé".

- Nick

13
Nick Alexeev

Vous devriez être capable d'accomplir le même genre de choses que "friend" en C++ en utilisant des interfaces en C #. Cela nécessite que vous définissiez explicitement quels membres sont passés entre les deux classes, ce qui représente un travail supplémentaire mais peut également faciliter la compréhension du code.

Si quelqu'un a un exemple d'utilisation raisonnable d '"ami" qui ne peut pas être simulé à l'aide d'interfaces, veuillez le partager! J'aimerais mieux comprendre les différences entre C++ et C #.

13
Parappa

En fait, C # donne la possibilité d'obtenir le même comportement de manière pure OOP sans mots spéciaux - ce sont des interfaces privées.

En ce qui concerne la question Quel est l'équivalent C de ami? a été marqué comme une copie de cet article et personne ne propose vraiment une bonne réalisation - je montrerai la réponse aux deux questions ici.

L'idée principale partait d'ici: Qu'est-ce qu'une interface privée?

Disons qu'il nous faut une classe capable de gérer les instances d'une autre classe et d'appeler des méthodes spéciales sur celles-ci. Nous ne voulons pas donner la possibilité d'appeler cette méthode à d'autres classes. C’est exactement la même chose que le mot clé ami c ++ dans le monde c ++.

Je pense que, dans la pratique, le bon exemple pourrait consister en un modèle de machine à états complète, dans lequel un contrôleur met à jour l’objet d’état actuel et bascule vers un autre objet d’état lorsque cela est nécessaire.

Vous pourriez:

  • La façon la plus simple et la plus mauvaise de rendre la méthode Update () publique - espérez que tout le monde comprendra pourquoi c'est mauvais.
  • La prochaine façon est de le marquer comme interne. C’est suffisant si vous placez vos classes dans une autre Assemblée, mais même dans ce cas, chaque classe de cette Assemblée pourrait appeler chaque méthode interne.
  • Utiliser une interface privée/protégée - et j'ai suivi cette voie.

Controller.cs

public class Controller
{
    private interface IState
    {
        void Update();
    }

    public class StateBase : IState
    {
        void IState.Update() {  }
    }

    public Controller()
    {
        //it's only way call Update is to cast obj to IState
        IState obj = new StateBase();
        obj.Update();
    }
}

Program.cs

class Program
{
    static void Main(string[] args)
    {
        //it's impossible to write Controller.IState p = new StateBase();
        //Controller.IState is hidden
        StateBase p = new StateBase();
        //p.Update(); //is not accessible
    }
}

Eh bien, qu'en est-il de l'héritage?

Nous devons utiliser la technique décrite dans étant donné que les implémentations de membres d'interface explicites ne peuvent pas être déclarées virtuelles et marquer IState comme protégé pour permettre également de dériver de Controller.

Controller.cs

public class Controller
{
    protected interface IState
    {
        void Update();
    }

    public class StateBase : IState
    {
        void IState.Update() { OnUpdate(); }
        protected virtual void OnUpdate()
        {
            Console.WriteLine("StateBase.OnUpdate()");
        }
    }

    public Controller()
    {
        IState obj = new PlayerIdleState();
        obj.Update();
    }
}

PlayerIdleState.cs

public class PlayerIdleState: Controller.StateBase
{
    protected override void OnUpdate()
    {
        base.OnUpdate();
        Console.WriteLine("PlayerIdleState.OnUpdate()");
    }
}

Et enfin un exemple sur la façon de tester l’héritage de la classe Controller: ControllerTest.cs

class ControllerTest: Controller
{
    public ControllerTest()
    {
        IState testObj = new PlayerIdleState();
        testObj.Update();
    }
}

J'espère couvrir tous les cas et ma réponse a été utile.

11
max_cn

C # manque le mot-clé "ami" pour la même raison que sa destruction déterministe manquante. En changeant les conventions, les gens se sentent intelligents, comme si leurs nouvelles façons de faire étaient supérieures à celles de quelqu'un d'autre. C'est une question de fierté.

Dire que "les classes d'amis sont mauvaises" est aussi perspicace que d'autres déclarations non qualifiées telles que "n'utilise pas de gotos" ou "Linux est meilleur que Windows".

Le mot-clé "ami" associé à une classe proxy est un excellent moyen de n’exposer que certaines parties d'une classe à une autre classe spécifique. Une classe proxy peut agir comme une barrière de confiance contre toutes les autres classes. "public" n'autorise pas un tel ciblage, et utiliser "protected" pour obtenir l'effet avec l'héritage est difficile s'il n'y a pas de relation conceptuelle "est une".

8
Matthew

Vous pouvez vous rapprocher de "ami" C++ avec le mot clé C # "interne" .

8
jeffm

Friend est extrêmement utile pour écrire un test unitaire.

Bien que cela entraîne un coût pour polluer légèrement votre déclaration de classe, il s'agit également d'un rappel imposé par le compilateur de ce que les tests pourraient réellement concerner de l'état interne de la classe.

Un idiome très utile et propre que j’ai trouvé, c’est quand j’ai des cours d’usine, ce qui les rend amis des objets qu’ils créent qui ont un constructeur protégé. Plus précisément, c'était à l'époque où une seule usine était chargée de créer les objets de rendu correspondants pour les objets d'écriture de rapport, dans un environnement donné. Dans ce cas, vous disposez d'un point de connaissance unique sur la relation entre les classes de rédacteur de rapport (éléments tels que blocs d'images, bandes de présentation, en-têtes de page, etc.) et leurs objets de rendu correspondants.

8
Andy Dent

Ce n'est en fait pas un problème avec C #. C'est une limitation fondamentale en IL. C # est limité par cela, comme tout autre langage .Net qui cherche à être vérifiable. Cette limitation inclut également les classes gérées définies dans C++/CLI ( Spec section 20.5 ).

Cela étant dit, je pense que Nelson a une bonne explication quant à la raison pour laquelle c'est une mauvaise chose.

7
JaredPar

Arrêtez de chercher des excuses pour cette limitation. ami est mauvais, mais interne est bon? ils sont la même chose, seul cet ami vous donne un contrôle plus précis sur ceux qui sont autorisés à accéder et ceux qui ne le sont pas.

C’est pour imposer le paradigme de l’encapsulation? il faut donc écrire des méthodes d'accès et maintenant quoi? comment êtes-vous censé empêcher tout le monde (à l'exception des méthodes de classe B) d'appeler ces méthodes? vous ne pouvez pas, parce que vous ne pouvez pas contrôler ça non plus, à cause de "ami" manquant.

Aucun langage de programmation n'est parfait. C # est l’un des meilleurs langages que j’ai vu, mais faire des excuses idiotes pour des fonctionnalités manquantes n’aide personne. En C++, je manque le système événementiel/délégué easy, la réflexion (+ la dé/sérialisation automatique) et le foreach, mais en C #, la surcharge des opérateurs (ouais, continue de me dire que tu n'en avais pas besoin), les paramètres par défaut, une constante cela ne peut pas être contourné, héritage multiple (oui, continuez à me dire que vous n'en avez pas besoin et que les interfaces étaient un remplacement suffisant) et à la possibilité de décider de supprimer une instance de la mémoire (non, ce n'est pas horriblement mauvais sauf si vous êtes un bricoleur)

4
Algoman

J'ai lu beaucoup de commentaires intelligents sur le mot clé "ami" et je suis d'accord sur ce qui est utile, mais je pense que le mot clé "interne" est moins utile, et ils sont toujours mauvais pour la programmation pure OO .

Ce que nous avons? (en disant de "ami" je dis aussi de "interne")

  • l'utilisation de "ami" rend le code moins pur en ce qui concerne oo?
  • oui;

  • "ami" n'améliore-t-il pas le code?

  • non, nous avons encore besoin de créer des relations privées entre les classes, et nous ne pouvons le faire que si nous brisons notre belle encapsulation, donc ce n’est pas bon non plus, je ne peux même pas dire ce que c’est encore plus mal que d’utiliser "ami".

Utiliser friend crée des problèmes locaux, ne pas l'utiliser crée des problèmes pour les utilisateurs de bibliothèques de codes.

la bonne solution commune pour le langage de programmation que je vois comme ceci:

// c++ style
class Foo {
  public_for Bar:
    void addBar(Bar *bar) { }
  public:
  private:
  protected:
};

// c#
class Foo {
    public_for Bar void addBar(Bar bar) { }
}

Qu'est-ce que tu en penses? Je pense que c'est la solution orientée objet la plus courante et la plus pure. Vous pouvez ouvrir l'accès à n'importe quelle méthode que vous choisissez pour la classe de votre choix.

2
FadeToBlack

Je répondrai seulement à la question "Comment".

Il y a tellement de réponses ici, cependant je voudrais proposer une sorte de "modèle de conception" pour réaliser cette fonctionnalité. Je vais utiliser un mécanisme de langage simple, qui comprend:

  • Des interfaces
  • Classe imbriquée

Par exemple, nous avons 2 classes principales: étudiant et université. L'étudiant a GPA auquel seule l'université est autorisée à accéder. Voici le code:

public interface IStudentFriend
{
    Student Stu { get; set; }
    double GetGPS();
}

public class Student
{
    // this is private member that I expose to friend only
    double GPS { get; set; }
    public string Name { get; set; }

    PrivateData privateData;

    public Student(string name, double gps) => (GPS, Name, privateData) = (gps, name, new PrivateData(this);

    // No one can instantiate this class, but Student
    // Calling it is possible via the IStudentFriend interface
    class PrivateData : IStudentFriend
    {
        public Student Stu { get; set; }

        public PrivateData(Student stu) => Stu = stu;
        public double GetGPS() => Stu.GPS;
    }

    // This is how I "mark" who is Students "friend"
    public void RegisterFriend(University friend) => friend.Register(privateData);
}

public class University
{
    var studentsFriends = new List<IStudentFriend>();

    public void Register(IStudentFriend friendMethod) => studentsFriends.Add(friendMethod);

    public void PrintAllStudentsGPS()
    {
        foreach (var stu in studentsFriends)
            Console.WriteLine($"{stu.Stu.Name}: stu.GetGPS()");
    }
}

public static void Main(string[] args)
{
    var Technion = new University();
    var Alex     = new Student("Alex", 98);
    var Jo       = new Student("Jo", 91);

    Alex.RegisterFriend(Technion);
    Jo.RegisterFriend(Technion);
    Technion.PrintAllStudentsGPS();

    Console.ReadLine();
}
2
Rami Yampolsky

Il y a InternalsVisibleToAttribute depuis .Net 3, mais je suppose qu'ils l'ont seulement ajouté pour permettre de tester des assemblages après la montée en puissance des tests unitaires. Je ne vois pas beaucoup d'autres raisons de l'utiliser.

Cela fonctionne au niveau de l’assemblée mais c’est ce qu’il fait au niveau interne; c'est-à-dire que vous souhaitez distribuer une assemblée tout en souhaitant qu'une autre assemblée non distribuée y ait un accès privilégié.

À juste titre, ils exigent que l’assemblée des amis soit forte afin d’éviter que quelqu'un crée un prétendu ami à côté de votre assemblée protégée.

2
Chris Woodward

Certains ont suggéré que les choses peuvent devenir incontrôlables en utilisant un ami. Je suis d’accord, mais cela ne diminue en rien son utilité. Je ne suis pas sûr que cet ami blesse nécessairement le paradigme OO), pas seulement de rendre tous les membres de votre classe publics. Certes, le langage vous permettra de rendre tous les membres publics, mais c'est un programmeur discipliné De même, un programmeur discipliné réserverait l'utilisation de friend à des cas spécifiques où cela fait sens. Je pense que, dans certains cas, interne est trop exposé. Pourquoi exposer une classe ou une méthode à tout ce qui se trouve dans l'assemblage?

J'ai une page ASP.NET qui hérite de ma propre page de base, qui hérite à son tour de System.Web.UI.Page. Dans cette page, j'ai un code qui gère le rapport d'erreurs de l'utilisateur final pour l'application dans une méthode protégée

ReportError("Uh Oh!");

Maintenant, j'ai un contrôle utilisateur qui est contenu dans la page. Je veux que le contrôle utilisateur puisse appeler les méthodes de rapport d'erreur dans la page.

MyBasePage bp = Page as MyBasePage;
bp.ReportError("Uh Oh");

Cela ne peut pas être fait si la méthode ReportError est protégée. Je peux le rendre interne, mais il est exposé à tout code de l'Assemblée. Je veux juste qu'il soit exposé aux éléments de l'interface utilisateur qui font partie de la page actuelle (y compris les contrôles enfants). Plus précisément, je souhaite que ma classe de contrôle de base définisse exactement les mêmes méthodes de rapport d'erreur et appelle simplement des méthodes dans la page de base.

protected void ReportError(string str) {
    MyBasePage bp = Page as MyBasePage;
    bp.ReportError(str);
}

Je pense que quelque chose comme ami pourrait être utile et implémenté dans le langage sans rendre le langage moins "OO" comme, peut-être en tant qu'attributs, de sorte que vous puissiez avoir des classes ou des méthodes être des amis pour des classes ou méthodes spécifiques, ce qui permet au développeur de fournir accès spécifique. Peut-être quelque chose comme ... (pseudo-code)

[Friend(B)]
class A {

    AMethod() { }

    [Friend(C)]
    ACMethod() { }
}

class B {
    BMethod() { A.AMethod() }
}

class C {
    CMethod() { A.ACMethod() }
}

Dans le cas de mon exemple précédent, j'ai peut-être quelque chose comme ceci (on peut discuter de la sémantique, mais j'essaie simplement de faire passer l'idée):

class BasePage {

    [Friend(BaseControl.ReportError(string)]
    protected void ReportError(string str) { }
}

class BaseControl {
    protected void ReportError(string str) {
        MyBasePage bp = Page as MyBasePage;
        bp.ReportError(str);
    }
}

Selon moi, le concept d'amis ne présente pas plus de risque que de rendre publiques des choses ou de créer des méthodes ou des propriétés publiques pour accéder aux membres. Si quelque chose ami permet un autre niveau de granularité dans l'accessibilité des données et vous permet de réduire cette accessibilité plutôt que de l'élargir à l'interne ou au public.

1
Scott Forbes

vous pouvez le garder privé et utiliser la réflexion pour appeler des fonctions. Le framework de test peut faire cela si vous lui demandez de tester une fonction privée

1
vishal

Je suppose que cela a quelque chose à voir avec le modèle de compilation C # - construire IL le JIT compilant cela au moment de l'exécution. C'est la même raison pour laquelle les génériques C # sont fondamentalement différents des génériques C++.

1
Stewart Johnson

L'amitié peut être simulée en séparant les interfaces et les implémentations. L'idée est la suivante: " Requiert une instance concrète mais restreint l'accès à la construction de cette instance ".

Par exemple

interface IFriend { }

class Friend : IFriend
{
    public static IFriend New() { return new Friend(); }
    private Friend() { }

    private void CallTheBody() 
    {  
        var body = new Body();
        body.ItsMeYourFriend(this);
    }
}

class Body
{ 
    public void ItsMeYourFriend(Friend onlyAccess) { }
}

En dépit du fait que ItsMeYourFriend() est publique, seule la classe Friend peut y accéder, car personne d'autre ne peut obtenir une instance concrète de la classe Friend. Il possède un constructeur privé, tandis que la méthode factory New() renvoie une interface.

Voir mon article Amis et membres de l'interface interne sans frais avec le codage aux interfaces pour plus de détails.

1
Reuven Bass

J'avais l'habitude d'utiliser régulièrement ami, et je ne pense pas que ce soit une violation de OOP ou un signe de défaut de conception. Il existe plusieurs endroits où c'est le moyen le plus efficace pour le bon but avec le moins de code.

Un exemple concret concerne la création d’assemblages d’interface fournissant une interface de communication à un autre logiciel. Généralement, quelques classes de poids lourds gèrent la complexité du protocole et les particularités des pairs, et fournissent un modèle relativement simple de connexion/lecture/écriture/transmission/déconnexion impliquant la transmission de messages et de notifications entre l'application cliente et l'Assembly. Ces messages/notifications doivent être encapsulés dans des classes. Les attributs doivent généralement être manipulés par le logiciel de protocole car il en est le créateur, mais beaucoup de choses doivent rester en lecture seule pour le monde extérieur.

Il est tout simplement ridicule de déclarer que c'est une violation de OOP pour que la classe de protocole/"créateur" ait un accès intime à toutes les classes créées - la classe de créateur a Ce que j’ai trouvé le plus important, c’est de minimiser toutes les lignes de code supplémentaires de BS auxquelles le modèle "POO for SOP" conduit habituellement. Des spaghettis supplémentaires ne font que créer plus de bugs.

Les gens savent-ils que vous pouvez appliquer le mot-clé internal au niveau de l'attribut, de la propriété et de la méthode? Ce n'est pas juste pour la déclaration de classe de niveau supérieur (bien que la plupart des exemples semblent le montrer.)

Si vous avez une classe C++ qui utilise le mot clé friend et souhaitez l'émuler dans une classe C #: 1. déclarez la classe C # publique 2. déclarez tous les attributs/propriétés/méthodes protégés en C++ et donc accessibles à vos amis en tant que internal en C # 3. créer des propriétés en lecture seule pour un accès public à tous les attributs et propriétés internes

Je conviens que ce n'est pas à 100% la même chose qu'un ami, et le test unitaire est un exemple très précieux du besoin de quelque chose comme un ami (comme le code de journalisation de l'analyseur de protocole). Cependant, internal fournit l'exposition aux classes auxquelles vous souhaitez être exposé, et [InternalVisibleTo ()] gère le reste - semble avoir été créé spécifiquement pour le test unitaire.

En ce qui concerne ami "étant meilleur parce que vous pouvez contrôler explicitement les classes qui ont accès" - que diable fait-il en masse pour un groupe de classes pervers suspectes dans la même assemblée? Partitionnez vos assemblées!

1
Christo

Si vous travaillez avec C++ et que vous vous utilisez en utilisant un mot clé friend, c'est un signe très fort que vous avez un problème de conception, car pourquoi une classe a-t-elle besoin d'accéder aux membres privés d'une autre classe?

0
bashmohandes

L'amitié peut également être simulée en utilisant des "agents" - certaines classes internes. Considérons l'exemple suivant:

public class A // Class that contains private members
{
  private class Accessor : B.BAgent // Implement accessor part of agent.
  {
    private A instance; // A instance for access to non-static members.
    static Accessor() 
    { // Init static accessors.
      B.BAgent.ABuilder = Builder;
      B.BAgent.PrivateStaticAccessor = StaticAccessor;
    }
    // Init non-static accessors.
    internal override void PrivateMethodAccessor() { instance.SomePrivateMethod(); }
    // Agent constructor for non-static members.
    internal Accessor(A instance) { this.instance = instance; }
    private static A Builder() { return new A(); }
    private static void StaticAccessor() { A.PrivateStatic(); }
  }
  public A(B friend) { B.Friendship(new A.Accessor(this)); }
  private A() { } // Private constructor that should be accessed only from B.
  private void SomePrivateMethod() { } // Private method that should be accessible from B.
  private static void PrivateStatic() { } // ... and static private method.
}
public class B
{
  // Agent for accessing A.
  internal abstract class BAgent
  {
    internal static Func<A> ABuilder; // Static members should be accessed only by delegates.
    internal static Action PrivateStaticAccessor;
    internal abstract void PrivateMethodAccessor(); // Non-static members may be accessed by delegates or by overrideable members.
  }
  internal static void Friendship(BAgent agent)
  {
    var a = BAgent.ABuilder(); // Access private constructor.
    BAgent.PrivateStaticAccessor(); // Access private static method.
    agent.PrivateMethodAccessor(); // Access private non-static member.
  }
}

Cela pourrait être beaucoup plus simple lorsqu'il est utilisé uniquement pour l'accès aux membres statiques. Les avantages d'une telle implémentation sont que tous les types sont déclarés dans la portée interne des classes d'amitié et, contrairement aux interfaces, cela permet d'accéder aux membres statiques.

0
Aberro

B.s.d.

Il a été dit que, les amis font mal OOness pur. Ce que je suis d'accord.

Il a également été déclaré que des amis aident à l'encapsulation, ce que je partage également.

Je pense que l'amitié devrait être ajoutée à la méthodologie OO, mais pas tout à fait comme en C++. J'aimerais avoir certains champs/méthodes auxquels ma classe d'amis peut accéder, mais je ne l'aurais PAS. Comme dans leur vie réelle, je laisserais mes amis accéder à mon réfrigérateur personnel, mais je ne les laisserais pas accéder à mon compte bancaire.

On peut mettre en œuvre comme suit

    class C1
    {
        private void MyMethod(double x, int i)
        {
            // some code
        }
        // the friend class would be able to call myMethod
        public void MyMethod(FriendClass F, double x, int i)
        {
            this.MyMethod(x, i);
        }
        //my friend class wouldn't have access to this method 
        private void MyVeryPrivateMethod(string s)
        {
            // some code
        }
    }
    class FriendClass
    {
        public void SomeMethod()
        {
            C1 c = new C1();
            c.MyMethod(this, 5.5, 3);
        }
    }

Cela générera bien sûr un avertissement pour le compilateur et nuira à l'intellisense. Mais ça va faire le travail.

Sur une note de côté, je pense qu'un programmeur confiant devrait faire l'unité de test sans accéder aux membres privés. c'est tout à fait hors de portée, mais essayez de lire sur le TDD. cependant, si vous voulez toujours le faire (avoir c ++ comme amis), essayez quelque chose comme

#if UNIT_TESTING
        public
#else
        private
#endif
            double x;

vous écrivez donc tout votre code sans définir UNIT_TESTING et lorsque vous souhaitez effectuer le test unitaire, ajoutez #define UNIT_TESTING à la première ligne du fichier (et écrivez tout le code qui effectue le test unitaire sous #if UNIT_TESTING). Cela devrait être traité avec soin.

Puisque je pense que les tests unitaires sont un mauvais exemple d'utilisation d'amis, je donnerais un exemple pour expliquer pourquoi je pense que les amis peuvent être bons. Supposons que vous ayez un système de rupture (classe). Avec l'utilisation, le système de freinage s'use et doit être rénové. Maintenant, vous voulez que seul un mécanicien agréé règle le problème. Pour rendre l'exemple moins trivial, je dirais que le mécanicien utiliserait son tournevis personnel (privé) pour le réparer. C'est pourquoi la classe mécanicien devrait être l'amie de la classe système.

0
ehud117