friendship friends example and c# c++ design-patterns oop friend

c# - friends - friend modifier c++



¿Por qué C#no proporciona la palabra clave "amigo" del estilo C++? (23)

La palabra clave de amigo C ++ permite que una class A designe a la class B como su amigo. Esto permite que la Class B acceda a los miembros private / protected de la class A

Nunca he leído nada sobre por qué se dejó esto fuera de C # (y VB.NET). La mayoría de las respuestas a esta pregunta anterior de StackOverflow parecen estar diciendo que es una parte útil de C ++ y hay buenas razones para usarla. En mi experiencia tendría que estar de acuerdo.

Otra question me parece que realmente está preguntando cómo hacer algo similar a un friend en una aplicación de C #. Si bien las respuestas generalmente giran en torno a clases anidadas, no parece tan elegante como usar la palabra clave friend .

El libro original de Design Patterns lo usa regularmente a lo largo de sus ejemplos.

Entonces, en resumen, ¿por qué falta un friend en C # y cuál es la forma (o las mejores) formas de "mejor práctica" de simularlo en C #?

(Por cierto, la palabra clave internal no es lo mismo, permite que todas las clases de todo el ensamblaje tengan acceso a internal miembros internal , mientras que el friend permite dar a una cierta clase acceso completo a otra clase)


A C # le falta la palabra clave "amigo" por la misma razón que falta la destrucción determinista. Cambiar las convenciones hace que las personas se sientan inteligentes, como si sus nuevas formas fueran superiores a las antiguas de otra persona. Se trata de orgullo.

Decir que "las clases de amigos son malas" es tan miope como otras afirmaciones no calificadas como "no usar gotos" o "Linux es mejor que Windows".

La palabra clave "amigo" combinada con una clase proxy es una excelente manera de exponer ciertas partes de una clase a otras clases específicas. Una clase proxy puede actuar como una barrera de confianza contra todas las demás clases. "público" no permite ninguna orientación de este tipo, y usar "protegido" para obtener el efecto de la herencia es incómodo si realmente no hay una relación conceptual "es una".


Algunos han sugerido que las cosas pueden salirse de control mediante el uso de un amigo. Estoy de acuerdo, pero eso no disminuye su utilidad. No estoy seguro de que el amigo dañe necesariamente el paradigma OO más que hacer públicos a todos los miembros de su clase. Ciertamente, el lenguaje le permitirá hacer públicos a todos sus miembros, pero es un programador disciplinado que evita ese tipo de patrón de diseño. Del mismo modo, un programador disciplinado reservaría el uso de amigos para casos específicos en los que tenga sentido. Siento que las exposiciones internas demasiado en algunos casos. ¿Por qué exponer una clase o método a todo en el ensamblaje?

Tengo una página ASP.NET que hereda mi propia página base, que a su vez hereda System.Web.UI.Page. En esta página, tengo un código que maneja el informe de errores del usuario final para la aplicación en un método protegido

ReportError("Uh Oh!");

Ahora, tengo un control de usuario que está contenido en la página. Quiero que el control de usuario pueda llamar a los métodos de informe de errores en la página.

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

No puede hacer eso si el método ReportError está protegido. Puedo hacerlo interno, pero está expuesto a cualquier código en el ensamblaje. Solo quiero que esté expuesto a los elementos de la interfaz de usuario que forman parte de la página actual (incluidos los controles secundarios). Más específicamente, quiero que mi clase de control base defina exactamente los mismos métodos de informe de errores y simplemente llame a los métodos en la página base.

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

Creo que algo como amigo podría ser útil e implementado en el idioma sin hacer que el lenguaje sea menos "OO", tal vez como atributos, para que pueda tener clases o métodos para ser amigos de clases o métodos específicos, lo que permite al desarrollador brindar Acceso específico. Quizás algo como ... (pseudo código)

[Friend(B)] class A { AMethod() { } [Friend(C)] ACMethod() { } } class B { BMethod() { A.AMethod() } } class C { CMethod() { A.ACMethod() } }

En el caso de mi ejemplo anterior, quizás tenga algo como lo siguiente (se puede argumentar semántica, pero solo estoy tratando de hacer entender la idea):

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

Como lo veo, el concepto de amigo no tiene más riesgo que hacer cosas públicas, o crear métodos o propiedades públicas para acceder a los miembros. Si algo, un amigo permite otro nivel de granularidad en la accesibilidad de los datos y le permite limitar esa accesibilidad en lugar de ampliarla con información interna o pública.


Con un friend un diseñador de C ++ tiene un control preciso sobre a quién están expuestos los miembros privados *. Pero, se ve obligado a exponer a cada uno de los miembros privados.

Con un diseñador internal C # tiene un control preciso sobre el conjunto de miembros privados que está exponiendo. Obviamente, él puede exponer a un solo miembro privado. Pero, será expuesto a todas las clases en la asamblea.

Normalmente, un diseñador desea exponer solo algunos métodos privados a algunas otras clases seleccionadas. Por ejemplo, en un patrón de fábrica de clase puede desearse que la clase C1 sea instanciada solo por la clase de fábrica CF1. Por lo tanto, la clase C1 puede tener un constructor protegido y una fábrica de clase amiga CF1.

Como puede ver, tenemos 2 dimensiones a lo largo de las cuales se puede romper la encapsulación. friend rompe en una dimensión, el internal hace en la otra. ¿Cuál es una brecha peor en el concepto de encapsulación? Difícil de decir. Pero sería bueno tener tanto friend como internal disponibles. Además, una buena adición a estos dos sería el tercer tipo de palabra clave, que se usaría miembro por miembro (como internal ) y especifica la clase objetivo (como friend ).

* Por brevedad, usaré "privado" en lugar de "privado y / o protegido".

- Nick


De hecho, C # da la posibilidad de obtener el mismo comportamiento en modo OOP puro sin palabras especiales, son interfaces privadas.

En cuanto a la pregunta ¿Cuál es el equivalente C # de amigo? Se marcó como duplicado en este artículo y nadie propone una realización realmente buena; mostraré la respuesta en ambas preguntas aquí.

La idea principal se tomó desde aquí: ¿Qué es una interfaz privada?

Digamos, necesitamos alguna clase que pueda administrar instancias de otras clases y llamar a algunos métodos especiales en ellas. No queremos dar la posibilidad de llamar a estos métodos a otras clases. Esto es exactamente lo mismo que hacer con la palabra clave c ++ del amigo en c ++ world.

Creo que un buen ejemplo en la práctica real podría ser el patrón de Full State Machine en el que algunos controladores actualizan el objeto de estado actual y cambian a otro objeto de estado cuando sea necesario.

Tú podrías:

  • La forma más fácil y peor de hacer público el método Update (): espero que todos entiendan por qué es malo.
  • La siguiente forma es marcarlo como interno. Es lo suficientemente bueno si coloca sus clases en otro ensamblaje, pero incluso entonces cada clase en ese ensamblaje podría llamar a cada método interno.
  • Usar interfaz privada / protegida - y seguí de esta manera.

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

Programa.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 } }

Bueno, ¿qué pasa con la herencia?

Necesitamos usar la técnica descrita en Ya que las implementaciones de miembros de la interfaz explícita no se pueden declarar virtuales y marcar IState como protegido para dar la posibilidad de derivar del Controlador también.

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

Y, por último, un ejemplo de cómo probar la herencia de lanzamiento del controlador de clase: ControllerTest.cs

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

Espero que cubra todos los casos y mi respuesta fue útil.


Debería poder lograr el mismo tipo de cosas para las que se usa "amigo" en C ++ utilizando interfaces en C #. Requiere que defina explícitamente qué miembros se están pasando entre las dos clases, lo que es un trabajo adicional, pero también puede hacer que el código sea más fácil de entender.

Si alguien tiene un ejemplo de un uso razonable de "amigo" que no se puede simular mediante interfaces, ¡compártelo! Me gustaría entender mejor las diferencias entre C ++ y C #.


Deja de poner excusas para esta limitación. amigo es malo, pero interno es bueno? son lo mismo, solo que un amigo te da un control más preciso sobre quién puede acceder y quién no.

Esto es para hacer cumplir el paradigma de encapsulación? ¿Así que tienes que escribir métodos de acceso y ahora qué? ¿Cómo se supone que debes evitar que todos (excepto los métodos de la clase B) llamen a estos métodos? No puedes, porque tampoco puedes controlar esto, porque falta un "amigo".

Ningún lenguaje de programación es perfecto. C # es uno de los mejores idiomas que he visto, pero hacer excusas tontas para las características que faltan no ayuda a nadie. En C ++, echo de menos el sistema fácil de delegado / evento, reflexión (+ des / serialización automática) y foreach, pero en C # echo de menos la sobrecarga del operador (sí, siga diciéndome que no la necesitaba), los parámetros predeterminados, una constante eso no puede ser evitado, herencia múltiple (sí, siga diciéndome que no la necesitaba y las interfaces fueron un reemplazo suficiente) y la capacidad de decidir eliminar una instancia de la memoria (no, esto no es terriblemente malo a menos que sea un tinkerer)


El amigo es extremadamente útil al escribir la prueba unitaria.

Si bien eso conlleva un costo de contaminar un poco su declaración de clase, también es un recordatorio forzado por el compilador de qué pruebas podrían realmente preocuparse por el estado interno de la clase.

Un lenguaje muy útil y limpio que he encontrado es cuando tengo clases de fábrica, haciéndoles amigos de los artículos que crean que tienen un constructor protegido. Más específicamente, esto fue cuando tuve una única fábrica responsable de crear objetos de representación coincidentes para los objetos del escritor de informes, la representación en un entorno determinado. En este caso, tiene un único punto de conocimiento acerca de la relación entre las clases de informe-escritor (elementos como bloques de imágenes, bandas de diseño, encabezados de página, etc.) y sus objetos de representación coincidentes.


En otros comentarios. Usar a un amigo no se trata de violar la encapsulación, sino que, por el contrario, se trata de imponerla. Al igual que los accessors + mutators, la sobrecarga de operadores, la herencia pública, el downcasting, etc. , a menudo se usa incorrectamente, pero no significa que la palabra clave no tenga, o peor aún, un mal propósito.

Vea el message Konrad Rudolph en el otro hilo, o si lo prefiere, vea la entrada relevante en las Preguntas frecuentes de C ++.


Esto no es realmente un problema con C #. Es una limitación fundamental en IL. C # está limitado por esto, al igual que cualquier otro lenguaje .Net que busque ser verificable. Esta limitación también incluye las clases administradas definidas en C ++ / CLI ( sección de especificaciones 20.5 ).

Dicho esto, creo que Nelson tiene una buena explicación de por qué esto es algo malo.


Existe el atributo InternalsVisibleToAttribute desde .Net 3, pero sospecho que solo lo agregaron para atender los ensamblajes de prueba después del aumento de las pruebas unitarias. No puedo ver muchas otras razones para usarlo.

Funciona a nivel de ensamblaje pero hace el trabajo donde no lo hace interno; es decir, donde desea distribuir un conjunto pero desea que otro conjunto no distribuido tenga acceso privilegiado a él.

Con toda razón, requieren que la asamblea de amigos tenga una clave sólida para evitar que alguien cree un amigo ficticio junto a su asamblea protegida.


He leído muchos comentarios inteligentes sobre la palabra clave "amigo" y estoy de acuerdo con lo que es útil, pero creo que la palabra clave "interna" es menos útil, y ambas siguen siendo malas para la programación OO pura.

¿Que tenemos? (diciendo acerca de "amigo" también digo acerca de "interno")

  • ¿Está utilizando "amigo" hace que el código sea menos puro con respecto a oo?
  • sí;

  • no está utilizando "amigo" hace que el código sea mejor?

  • no, todavía necesitamos establecer algunas relaciones privadas entre clases, y podemos hacerlo solo si rompemos nuestra hermosa encapsulación, por lo que tampoco es bueno, puedo decir lo que es aún más malvado que usar "amigo".

El uso de amigos crea algunos problemas locales, no usarlo crea problemas para los usuarios de la biblioteca de códigos.

La buena solución común para el lenguaje de programación que veo así:

// 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é piensa usted al respecto? Creo que es la solución más común y pura orientada a objetos. Puede abrir el acceso a cualquier método que elija a la clase que desee.


La amistad puede simularse separando interfaces e implementaciones. La idea es: " Requerir una instancia concreta pero restringir el acceso de construcción de esa instancia ".

Por ejemplo

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) { } }

A pesar de que ItsMeYourFriend() es público, solo la clase Friend puede acceder a ella, ya que nadie más puede obtener una instancia concreta de la clase Friend . Tiene un constructor privado, mientras que el método factory New() devuelve una interfaz.

Vea mi artículo Amigos y miembros de la interfaz interna sin costo con codificación a las interfaces para obtener más detalles.


Para información, otra cosa relacionada, pero no exactamente igual en .NET es [InternalsVisibleTo] , que permite que un conjunto designe otro conjunto (como un conjunto de prueba de unidad) que (efectivamente) tiene acceso "interno" a los tipos / Miembros en la asamblea original.


Puede acercarse a C ++ "amigo" con la palabra clave C # "internal" .


Puedes mantenerlo privado y usar la reflexión para llamar a funciones. El marco de prueba puede hacer esto si le pide que pruebe una función privada


Si está trabajando con C ++ y se encuentra usando una palabra clave de amigo, es una indicación muy clara de que tiene un problema de diseño, ¿por qué diablos necesita una clase acceder a los miembros privados de otra clase?


Solía ​​usar regularmente a un amigo, y no creo que sea una violación de la POO o un signo de algún defecto de diseño. Hay varios lugares donde es el medio más eficiente para el fin apropiado con la menor cantidad de código.

Un ejemplo concreto es cuando se crean conjuntos de interfaces que proporcionan una interfaz de comunicaciones a algún otro software. En general, hay algunas clases pesadas que manejan la complejidad del protocolo y las peculiaridades de los compañeros, y proporcionan un modelo relativamente simple de conexión / lectura / escritura / avance / desconexión que implica el paso de mensajes y notificaciones entre la aplicación cliente y el ensamblaje. Esos mensajes / notificaciones deben ser envueltos en clases. Generalmente, los atributos deben ser manipulados por el software de protocolo, ya que es su creador, pero muchas cosas deben permanecer de solo lectura para el mundo exterior.

Es simplemente una tontería declarar que es una violación de la POO para que la clase de protocolo / "creador" tenga un acceso íntimo a todas las clases creadas; la clase creadora ha tenido que morder cada bit de datos en el camino hacia arriba. Lo que he encontrado más importante es minimizar todas las líneas adicionales de código BS a las que generalmente conduce el modelo "OOP for OOP''s Sake". Los espaguetis extra solo hacen más bichos.

¿Sabe la gente que puede aplicar la palabra clave interna a nivel de atributo, propiedad y método? No es solo para la declaración de clase de nivel superior (aunque la mayoría de los ejemplos parecen mostrar eso).

Si tiene una clase de C ++ que usa la palabra clave de amigo y desea emular eso en una clase de C #: 1. declare la clase de C # pública 2. declare todos los atributos / propiedades / métodos que están protegidos en C ++ y, por lo tanto, accesibles a amigos como interno en C # 3. cree propiedades de solo lectura para el acceso público a todos los atributos y propiedades internos

Estoy de acuerdo en que no es 100% lo mismo que un amigo, y la prueba unitaria es un ejemplo muy valioso de la necesidad de algo así como un amigo (como es el código de registro del analizador de protocolo). Sin embargo, interno proporciona la exposición a las clases que desea tener, y [InternalVisibleTo ()] maneja el resto; parece que nació específicamente para la prueba de unidad.

En lo que respecta a los amigos, "ser mejor porque puedes controlar explícitamente a qué clases tienen acceso". ¿Qué diablos hacen un grupo de clases malintencionadas sospechosas en el mismo ensamblaje en primer lugar? ¡Particiona tus asambleas!


Sospecho que tiene algo que ver con el modelo de compilación de C #: compilar IL el JIT compilando eso en tiempo de ejecución. es decir: la misma razón por la que los genéricos de C # son fundamentalmente diferentes a los genéricos de C ++.


Tener amigos en la programación se considera más o menos "sucio" y fácil de abusar. Rompe las relaciones entre las clases y socava algunos atributos fundamentales de un lenguaje OO.

Dicho esto, es una buena característica y la he usado muchas veces en C ++; y me gustaría usarlo en C # también. Pero apuesto a que debido a la OOness "pura" de C # (en comparación con la pseudo OOness de C ++) MS decidió que Java no tiene una palabra clave de amigo C # tampoco debería (solo es broma;))

En una nota seria: interno no es tan bueno como un amigo, pero hace el trabajo. Recuerde que es raro que distribuya su código a desarrolladores externos a través de una DLL; por lo tanto, siempre que usted y su equipo conozcan las clases internas y su uso, deberían estar bien.

EDITAR Permítame aclarar cómo la palabra clave de un amigo socava la POO.

Las variables y los métodos privados y protegidos son quizás una de las partes más importantes de la POO. La idea de que los objetos pueden contener datos o la lógica que solo ellos pueden usar le permite escribir su implementación de funcionalidad independiente de su entorno, y que su entorno no puede alterar la información de estado que no es adecuada para manejar. Al usar Friend, estás acoplando implementaciones de dos clases, lo que es mucho peor que si solo hubieras acoplado su interfaz.


Voy a responder sólo a la pregunta "Cómo".

Hay tantas respuestas aquí, sin embargo, me gustaría proponer un tipo de "patrón de diseño" para lograr esa característica. Usaré un mecanismo de lenguaje simple, que incluye:

  • Interfaces
  • Clase anidada

Por ejemplo tenemos 2 clases principales: Estudiante y Universidad. El estudiante tiene un GPA al que solo la universidad puede acceder. Aquí está el código:

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


Bsd

Se afirmó que, los amigos lastiman la pureza pura. Que estoy de acuerdo.

También se dijo que los amigos ayudan a la encapsulación, lo cual también estoy de acuerdo.

Creo que la amistad debería agregarse a la metodología OO, pero no exactamente como en C ++. Me gustaría tener algunos campos / métodos a los que pueda acceder mi clase de amigos, pero NO me gustaría que accedan a TODOS mis campos / métodos. Como en la vida real, dejaba que mis amigos accedieran a mi refrigerador personal pero no les permitía acceder a mi cuenta bancaria.

Uno puede implementar eso como sigue

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

Eso, por supuesto, generará una advertencia del compilador, y dañará la inteligencia. Pero hará el trabajo.

Como nota al margen, creo que un programador seguro debe hacer la unidad de prueba sin acceder a los miembros privados. Esto está bastante fuera del alcance, pero trate de leer sobre TDD. sin embargo, si todavía quieres hacerlo (tener c ++ como amigos) intenta algo como

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

así que escribe todo su código sin definir UNIT_TESTING y cuando quiere hacer la prueba de unidad, agregue #define UNIT_TESTING a la primera línea del archivo (y escriba todo el código que hace la prueba de unidad bajo #if UNIT_TESTING). Eso debe ser manejado con cuidado.

Como creo que las pruebas de unidad son un mal ejemplo para el uso de amigos, daré un ejemplo de por qué creo que los amigos pueden ser buenos. Supongamos que tienes un sistema de ruptura (clase). Con el uso, el sistema de rotura se desgasta y necesita ser renovado. Ahora, quieres que solo un mecánico con licencia lo arregle. Para hacer el ejemplo menos trivial, diría que el mecánico usaría su destornillador personal (privado) para solucionarlo. Es por eso que la clase mecánica debe ser amiga de la clase breakSystem.


La amistad también puede simularse utilizando "agentes", algunas clases internas. Considere el siguiente ejemplo:

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

Podría ser mucho más simple cuando se usa para acceder solo a miembros estáticos. Los beneficios de dicha implementación son que todos los tipos se declaran en el ámbito interno de las clases de amistad y, a diferencia de las interfaces, permite el acceso a miembros estáticos.


Los campos que TODAS las clases pueden acceder a ellos son public.

Los campos que NO todas las demás clases pueden acceder a ellos son private.

(Si los campos pertenecen a la clase base (declarada dentro) , entonces son en su protectedlugar)

Los campos a los que solo puede acceder su clase de propietario son private, y no tienen propiedades y obtienen métodos establecidos.

Los campos que solo su clase de propietario y algunas otras clases pueden acceder a ellos son privatey cada uno tiene privatemétodos especiales de configuración y obtención, y publicmétodos compartidos.

Las otras clases que también pueden acceder a estos campos tendrán algunos privatecampos de tipo delegado y publicmétodos directos especiales .

Ejemplo:

class A { private int integer; // In the meantime, it seems that only A can access this integer private int GetInteger() // Get method is not public, because we don''t want all other classes to use this integer { return this.integer; } private void SetInteger(int value) //Set method is not public, because we don''t want all other classes to modify this integer { this.integer = value; } public void Share(ref B b) //I use the ''ref'' keyword, to prevent the ''null'' value in this argument, but if you call this method in a constructor of B, or in one of its methods, so you will have to remove the ''ref'' keyword, or make overload of the same method without the ''ref'' keyword, because ''ref this'' is not allowed { b.DirectGetIntegerAndSetIntegerMethods(this.GetInteger, this.SetInteger); } public void PrintInteger() { Console.WriteLine(this.integer); } } class B //This class can access the ''integer'' of A too, i.e. the ''integer'' of A is "public" only for B { private Func<int> GetInteger; //Will execute the ''GetInteger'' method of A private Action<int> SetInteger; //Will execute the ''SetInteger'' method of A public void DirectGetIntegerAndSetIntegerMethods(Func<int> getInteger, Action<int> setInteger) { this.GetInteger = getInteger; this.SetInteger = setInteger; } public void Increment() { this.SetInteger(this.GetInteger() + 1); } } class Program { static void Main(string[] args) { A a = new A(); //Created new instance of A, and also new Int32 was initialized inside it and set to its default value 0, but unable to get or set its value, only just print it. a.PrintInteger(); B b = new B(); //Must create new instance of B, in order to change the integer of A to some value. For example, I will use b, to change the integer of a to 3 a.Share(ref b); //But before the change, I must tell ''a'' to share his GetInteger and SetInteger methods to ''b'', so ''b'' will also be able execute them, through his Func<int> and Action<int> delegates, because GetInteger and SetInteger methods of A are private and cannot be executed directly. for (int i = 0; i < 3; i++) b.Increment(); a.PrintInteger(); //Print the integer of ''a'' again after change. //Now the output of the console is: //0 //3 } }

Debe saber que usar la palabra clave ''amigo'' de C ++ es permitir que algunas clases compartan sus miembros privados con otras clases directamente .

Debido a que la palabra clave "amigo" no existe en C #, las clases no tienen forma de compartir sus campos privados directamente con otras clases , pero hay una manera de simularlo, como se muestra arriba.

¿Sabe que la palabra clave ''amigo'' de C ++ también puede permitir en la implementación de algunas funciones acceder a los miembros privados de algunas instancias de algunos tipos de clases?

La respuesta es sí y te mostraré cómo simular esto también en C #:

Ejemplo:

using System; using System.Reflection; //New namespace is added using System.Diagnostics; //Another new namespace is added too class Person { private readonly StackTrace st = new StackTrace(); //Helper object private readonly MethodInfo mi = typeof(Program).GetMethod("Foo"); //The MethodInfo of the method Foo, which will be declared and defined in class Program later, is the friend of the class Person //Both objects above are readonly, because they always reference the same objects that they were initialized with. private string name_of_his_dog; //Only Person can access this field private string GetTheNameOfHisDog() //Not public so that not all methods will be able to get this name { return this.name_of_his_dog; } private void SetTheNameOfHisDog(string new_name) //Not public so that not all methods will be able to set this name { this.name_of_his_dog = new_name; } public Func<string> ShareTheGetTheNameOfHisDogMethod() //Returns null, if the previous method that called this method is not friend of this class Person { if (this.st.GetFrame(1).GetMethod() == this.mi) return this.GetTheNameOfHisDog; return null; } public Action<string> ShareTheSetTheNameOfHisDogMethod() //Same as above { if (this.st.GetFrame(1).GetMethod() == this.mi) return this.SetTheNameOfHisDog; return null; } public void PrintTheNameOfHisDog() { Console.WriteLine(this.name_of_his_dog); } } class Program { static void Main(string[] args) { Person person = Foo(); person.PrintTheNameOfHisDog(); Func<string> getTheNameOfHisDog = person.ShareTheGetTheNameOfHisDogMethod(); //returns null, Main is not friend of Person Action<string> setTheNameOfHisDog = person.ShareTheSetTheNameOfHisDogMethod(); //returns null too, for the same reason setTheNameOfHisDog("Pointer"); //Runtime Error: Object reference not set to an instance of an object Console.WriteLine(getTheNameOfHisDog()); //Same runtime error //Output before runtime error: //Boxer //Boxer } public static Person Foo() //Only this method can get and set the name of the dog that belongs to the person { Person person = new Person(); Func<string> getTheNameOfHisDog = person.ShareTheGetTheNameOfHisDogMethod(); Action<string> setTheNameOfHisDog = person.ShareTheSetTheNameOfHisDogMethod(); setTheNameOfHisDog("Boxer"); Console.WriteLine(getTheNameOfHisDog()); return person; } }

Debo admitir que antes de publicar este código, no sabía cómo descubrir el MethodInfométodo anterior que llamaba al método actual, pero la respuesta de Firas Assaad me ayudó, gracias a él también.

¿Cómo puedo encontrar el método que llamó al método actual?

Sugirió usar la System.Diagnostics.StackTraceclase.

Espero que tengas mi idea, y eso ayude y responda a tu pregunta.

No encontré esta respuesta en ninguna parte de Internet, pensé en esta idea usando mi cerebro.