La palabra clave amigo de C ++ permite que class A
a se designe class B
como amigo. Esto permite Class B
acceder a los private
/ protected
miembros de class A
.
Nunca he leído nada sobre por qué esto se dejó 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 que hay buenas razones para usarla. En mi experiencia, tendría que estar de acuerdo.
Me parece que otra pregunta es realmente preguntar cómo hacer algo similar friend
en una aplicación C #. Si bien las respuestas generalmente giran en torno a clases anidadas, no parece tan elegante como usar la friend
palabra clave.
El libro original de Patrones de diseño lo usa regularmente en todos sus ejemplos.
En resumen, ¿por qué friend
falta en C # y cuál es la forma (o formas) de "mejores prácticas" de simularlo en C #?
(Por cierto, la internal
palabra clave no es la misma cosa, permite que todas las clases dentro de todo el ensamblado accedan a los internal
miembros, mientras friend
que le permite otorgar a cierta clase acceso completo a exactamente otra clase)
Respuestas:
Tener amigos en la programación se considera más o menos "sucio" y fácil de abusar. Rompe las relaciones entre 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 causa de la OOness "pura" de C # (en comparación con la pseudo OOness de C ++) MS decidió que debido a que Java no tiene una palabra clave amiga, C # tampoco debería (es broma;))
En una nota seria: interno no es tan bueno como amigo, pero hace el trabajo. Recuerde que es raro que distribuya su código a desarrolladores de terceros no a través de una DLL; así que mientras usted y su equipo conozcan las clases internas y su uso, estarán bien.
EDITAR Permítanme aclarar cómo la palabra clave amigo socava la POO.
Las variables y métodos privados y protegidos son quizás una de las partes más importantes de OOP. La idea de que los objetos pueden contener datos o lógica que solo ellos pueden usar le permite escribir su implementación de funcionalidad independientemente 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á acoplando las implementaciones de dos clases juntas, lo cual es mucho peor que si solo hubiera acoplado su interfaz.
fuente
friend
no permite que clases o funciones arbitrarias accedan a miembros privados. Permite que las funciones o clases específicas que se marcaron como amigo lo hagan. La clase propietaria del miembro privado aún controla el acceso al 100%. También podría argumentar que los métodos de miembros públicos. Ambos aseguran que los métodos enumerados específicamente en la clase tengan acceso a miembros privados de la clase.En otros comentarios. Usar friend no se trata de violar la encapsulación, sino de hacerla cumplir. Al igual que los accesores + mutadores, 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, un mal propósito.
Vea el mensaje de Konrad Rudolph en el otro hilo, o si lo prefiere, vea la entrada relevante en las preguntas frecuentes de C ++.
fuente
friend
le da acceso a TODOS sus miembros privados, incluso si solo necesita dejar que establezca uno de ellos. Hay un fuerte argumento para afirmar que hacer que los campos estén disponibles para otra clase que no los necesita cuenta como "violar la encapsulación". Hay lugares en C ++ donde es necesario (sobrecarga de operadores), pero hay una compensación de encapsulación que debe considerarse cuidadosamente. Parece que los diseñadores de C # sintieron que la compensación no valía la pena.Para obtener información, hay otra cosa relacionada, pero no exactamente la misma en .NET
[InternalsVisibleTo]
, que permite que un ensamblaje designe otro ensamblaje (como un ensamblaje de prueba de unidad) que (efectivamente) tenga acceso "interno" a tipos / miembros en El montaje original.fuente
Debería poder lograr el mismo tipo de cosas para las que se usa "amigo" en C ++ mediante el uso de interfaces en C #. Requiere que defina explícitamente qué miembros se pasan entre las dos clases, lo cual 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 uso razonable de "amigo" que no se puede simular usando interfaces, ¡compártelo! Me gustaría entender mejor las diferencias entre C ++ y C #.
fuente
Con
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
internal
un diseñador de C # tiene un control preciso sobre el conjunto de miembros privados que está exponiendo. Obviamente, puede exponer a un solo miembro privado. Pero, quedará expuesto a todas las clases en la asamblea.Por lo general, un diseñador desea exponer solo unos pocos métodos privados a algunas otras clases seleccionadas. Por ejemplo, en un patrón de clase de fábrica, 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 amiga de la clase CF1.
Como puede ver, tenemos 2 dimensiones a lo largo de las cuales se puede romper la encapsulación.
friend
lo rompe a lo largo de una dimensión, lointernal
hace a lo largo de la otra. ¿Cuál es una violación peor en el concepto de encapsulación? Difícil de decir. Pero sería bueno tener ambosfriend
yinternal
disponibles. Además, una buena adición a estos dos sería el tercer tipo de palabra clave, que se utilizaría miembro por miembro (likeinternal
) y especifica la clase de destino (likefriend
).* Por brevedad, usaré "privado" en lugar de "privado y / o protegido".
- Nick
fuente
De hecho, C # ofrece la posibilidad de obtener el mismo comportamiento en modo POO puro sin palabras especiales: son interfaces privadas.
En cuanto a la pregunta ¿Cuál es el equivalente de C # de amigo? se marcó como duplicado de este artículo y nadie propone una realización realmente buena. Aquí mostraré la respuesta a ambas preguntas.
La idea principal fue tomar desde aquí: ¿Qué es una interfaz privada?
Digamos que necesitamos alguna clase que pueda administrar instancias de otras clases y llamar a algunos métodos especiales sobre ellas. No queremos dar la posibilidad de llamar a estos métodos a ninguna otra clase. Esto es exactamente lo mismo que hace la palabra clave friend c ++ en el mundo c ++.
Creo que un buen ejemplo en la práctica real podría ser el patrón Full State Machine donde algunos controladores actualizan el objeto de estado actual y cambian a otro objeto de estado cuando es necesario.
Tú podrías:
Controller.cs
Program.cs
Bueno, ¿qué hay de la herencia?
Necesitamos usar la técnica descrita en Dado que las implementaciones explícitas de miembros de interfaz no pueden declararse virtuales y marcar IState como protegido para dar la posibilidad de derivar también del Controlador.
Controller.cs
PlayerIdleState.cs
Y, por último, un ejemplo de cómo probar la herencia de lanzamiento de controlador de clase: ControllerTest.cs
Espero cubrir todos los casos y mi respuesta fue útil.
fuente
Friend es extremadamente útil al escribir pruebas unitarias.
Si bien eso tiene un costo de contaminar ligeramente la declaración de su clase, también es un recordatorio forzado por el compilador de qué pruebas realmente podrían interesarle en el estado interno de la clase.
Un idioma muy útil y limpio que he encontrado es cuando tengo clases de fábrica, haciéndolos amigos de los artículos que crean que tienen un constructor protegido. Más específicamente, esto fue cuando tenía una única fábrica responsable de crear objetos de representación coincidentes para los objetos del escritor de informes, que representaban en un entorno determinado. En este caso, tiene un único punto de conocimiento sobre la relación entre las clases de redactor de informes (cosas como bloques de imágenes, bandas de diseño, encabezados de página, etc.) y sus objetos de representación coincidentes.
fuente
A C # le falta la palabra clave "amigo" por la misma razón que le falta la destrucción determinista. Cambiar las convenciones hace que las personas se sientan inteligentes, como si sus nuevas formas fueran superiores a las viejas. Se trata de orgullo.
Decir que "las clases de amigos son malas" es tan miope como otras declaraciones no calificadas como "no use 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 confiable contra todas las demás clases. "público" no permite ninguna focalización de este tipo, y usar "protegido" para obtener el efecto con la herencia es incómodo si realmente no existe una relación conceptual "es una".
fuente
Puede acercarse a C ++ "amigo" con la palabra clave C # "interno" .
fuente
internal
tiene mucho más sentido y proporciona una excelente compensación entre la encapsulación y la utilidad. Sin embargo, el acceso predeterminado de Java es aún mejor.Esto en realidad no es un problema con C #. Es una limitación fundamental en IL. C # está limitado por esto, al igual que cualquier otro lenguaje .Net que busca ser verificable. Esta limitación también incluye 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.
fuente
friend
no es algo malo por el contrario, es mucho mejor queinternal
. Y la explicación de Nelson es mala e incorrecta.Deja de poner excusas para esta limitación. amigo es malo, pero interno es bueno? son lo mismo, solo que ese amigo te da un control más preciso sobre a quién se le permite acceder y quién no.
Esto es para hacer cumplir el paradigma de encapsulación? entonces 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 te falta "amigo".
Ningún lenguaje de programación es perfecto. C # es uno de los mejores lenguajes que he visto, pero poner excusas tontas para las funciones faltantes no ayuda a nadie. En C ++, extraño el sistema fácil de eventos / delegados, reflexión (+ des / serialización automática) y foreach, pero en C # extraño la sobrecarga del operador (sí, sigue diciéndome que no lo necesitas), parámetros predeterminados, una constante eso no se puede eludir, herencia múltiple (sí, sigue diciéndome que no lo necesitabas y las interfaces eran un reemplazo suficiente) y la capacidad de decidir eliminar una instancia de la memoria (no, esto no es terriblemente malo a menos que seas un tinkerer)
fuente
||
/&&
mientras los mantiene en cortocircuito. Los parámetros predeterminados se agregaron en C # 4.Existe el InternalsVisibleToAttribute desde .Net 3, pero sospecho que solo lo agregaron para atender a los ensambles de prueba después del aumento de las pruebas unitarias. No veo muchas otras razones para usarlo.
Funciona a nivel de ensamblaje pero hace el trabajo donde no lo hace el interno; es decir, donde desea distribuir un ensamblado pero desea que otro ensamblado no distribuido tenga acceso privilegiado a él.
Con toda razón, requieren que el conjunto de amigos tenga una clave fuerte para evitar que alguien cree un amigo simulado junto con su conjunto protegido.
fuente
He leído muchos comentarios inteligentes sobre la palabra clave "amigo" y estoy de acuerdo en lo que es útil, pero creo que la palabra clave "interna" es menos útil, y ambos siguen siendo malos para la programación pura de OO.
¿Que tenemos? (diciendo sobre "amigo" También digo sobre "interno")
si;
no está usando "amigo" hace que el código sea mejor?
Usar friend 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í:
¿Qué piensa usted al respecto? Creo que es la solución orientada a objetos más común y pura. Puede abrir el acceso a cualquier método que elija a la clase que desee.
fuente
Solo responderé a la pregunta "Cómo".
Hay tantas respuestas aquí, sin embargo, me gustaría proponer una especie de "patrón de diseño" para lograr esa característica. Usaré un mecanismo de lenguaje simple, que incluye:
Por ejemplo, tenemos 2 clases principales: estudiante y universidad. El estudiante tiene un GPA al cual solo la universidad tiene acceso. Aquí está el código:
fuente
Sospecho que tiene algo que ver con el modelo de compilación C #: construir 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 ++.
fuente
puede mantenerlo privado y usar la reflexión para llamar a las funciones. Test Framework puede hacer esto si le pide que pruebe una función privada
fuente
Solía usar regularmente friend, y no creo que sea una violación de OOP o una señal de algún defecto de diseño. Hay varios lugares donde es el medio más eficiente para el fin adecuado con la menor cantidad de código.
Un ejemplo concreto es cuando se crean conjuntos de interfaz que proporcionan una interfaz de comunicaciones a otro software. En general, hay algunas clases de peso pesado que manejan la complejidad del protocolo y las peculiaridades de los pares, y proporcionan un modelo de conexión / lectura / escritura / reenvío / desconexión relativamente simple que implica pasar mensajes y notificaciones entre la aplicación del cliente y el ensamblado. Esos mensajes / notificaciones deben incluirse en clases. Los atributos generalmente deben ser manipulados por el software del protocolo, ya que es su creador, pero muchas cosas tienen que permanecer en modo de solo lectura para el mundo exterior.
Es simplemente una tontería declarar que es una violación de OOP que la clase de protocolo / "creador" tenga acceso íntimo a todas las clases creadas: la clase de creador ha tenido que manipular cada bit de datos en el camino. Lo que he encontrado más importante es minimizar todas las líneas adicionales de código BS al que suele conducir el modelo "OOP for OOP's Sake". Los espaguetis adicionales solo hacen más errores.
¿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 C ++ que usa la palabra clave friend y desea emularla en una clase C #: 1. declare pública la clase C # 2. declare todos los atributos / propiedades / métodos que están protegidos en C ++ y, por lo tanto, accesibles para amigos como interno en C # 3. crea 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 amigo, y la prueba unitaria es un ejemplo muy valioso de la necesidad de algo como 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 exposición, y [InternalVisibleTo ()] maneja el resto, parece que nació específicamente para la prueba unitaria.
En cuanto a amigo "ser mejor porque puedes controlar explícitamente qué clases tienen acceso", ¿qué diablos están haciendo un montón de clases malvadas sospechosas en la misma asamblea en primer lugar? ¡Particione sus ensamblajes!
fuente
La amistad se puede simular separando interfaces e implementaciones. La idea es: " Requerir una instancia concreta pero restringir el acceso de construcción de esa instancia ".
Por ejemplo
A pesar de que
ItsMeYourFriend()
es público, solo laFriend
clase puede acceder a él, ya que nadie más puede obtener una instancia concreta de laFriend
clase. Tiene un constructor privado, mientras que elNew()
método de fábrica devuelve una interfaz.Vea mi artículo Amigos y miembros de la interfaz interna sin costo alguno con la codificación de las interfaces para más detalles.
fuente
Algunos han sugerido que las cosas pueden salirse de control usando un amigo. Estoy de acuerdo, pero eso no disminuye su utilidad. No estoy seguro de que ese amigo necesariamente dañe el paradigma OO más que hacer públicos a todos los miembros de su clase. Ciertamente, el lenguaje le permitirá hacer públicos todos sus miembros, pero es un programador disciplinado que evita ese tipo de patrón de diseño. Del mismo modo, un programador disciplinado se reservaría el uso de amigo para casos específicos en los que tiene sentido. Siento exposiciones internas demasiado en algunos casos. ¿Por qué exponer una clase o método a todo en el ensamblado?
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 los informes de errores del usuario final para la aplicación en un método protegido
Ahora, tengo un control de usuario que está contenido en la página. Quiero que el control del usuario pueda llamar a los métodos de informe de errores en la página.
No puede hacer eso si el método ReportError está protegido. Puedo hacerlo interno, pero está expuesto a cualquier código en el ensamblado. 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.
Creo que algo como amigo podría ser útil e implementado en el lenguaje sin hacer que el lenguaje sea menos "OO", tal vez como atributos, para que pueda hacer que las clases o métodos sean amigos de clases o métodos específicos, lo que le permite al desarrollador proporcionar acceso específico Quizás algo como ... (pseudocódigo)
En el caso de mi ejemplo anterior, tal vez tenga algo como lo siguiente (uno puede discutir la semántica, pero solo estoy tratando de transmitir la idea):
Desde mi punto de vista, el concepto de amigo no tiene más riesgo que hacerlo público o crear métodos o propiedades públicas para acceder a los miembros. Si algo amigo permite otro nivel de granularidad en la accesibilidad de los datos y le permite reducir esa accesibilidad en lugar de ampliarla con información interna o pública.
fuente
Si está trabajando con C ++ y se encuentra usando la palabra clave amigo, es una indicación muy fuerte, que tiene un problema de diseño, porque ¿por qué diablos una clase necesita acceder a los miembros privados de otra clase?
fuente
Bsd
Se dijo que, los amigos duelen pura OOness. Que estoy de acuerdo.
También se dijo que los amigos ayudan a la encapsulación, lo que 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 mi clase amiga pueda acceder, pero NO me gustaría que accedan a TODOS mis campos / métodos. Como en la vida real, les permitía a mis amigos acceder a mi refrigerador personal, pero no les permitía acceder a mi cuenta bancaria.
Uno puede implementar eso de la siguiente manera
Eso, por supuesto, generará una advertencia del compilador y dañará el intellisense. Pero hará el trabajo.
En una 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 intente leer sobre TDD. sin embargo, si aún desea hacerlo (tener a C ++ como amigos) intente algo como
así que escribe todo su código sin definir UNIT_TESTING y cuando desea realizar la prueba de la unidad, agrega #define UNIT_TESTING a la primera línea del archivo (y escribe todo el código que realiza la prueba de la unidad en #if UNIT_TESTING). Eso debe manejarse con cuidado.
Como creo que las pruebas unitarias son un mal ejemplo para el uso de amigos, daría un ejemplo de por qué creo que los amigos pueden ser buenos. Supongamos que tiene un sistema de ruptura (clase). Con el uso, el sistema de ruptura se desgasta y necesita ser renovado. Ahora, desea 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 arreglarlo. Es por eso que la clase mecánica debería ser amiga de la clase breakingSystem.
fuente
c.MyMethod((FriendClass) null, 5.5, 3)
desde cualquier clase.La amistad también puede simularse mediante el uso de "agentes", algunas clases internas. Considere el siguiente ejemplo:
Podría ser mucho más simple cuando se usa para acceder solo a miembros estáticos. Los beneficios de dicha implementación es 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.
fuente