¿Por qué los campos privados son privados para el tipo, no para la instancia?


En C# (y muchos otros lenguajes) es perfectamente legítimo acceder a campos privados de otras instancias del mismo tipo. Por ejemplo:

public class Foo
{
    private bool aBool;

    public void DoBar(Foo anotherFoo)
    {
        if (anotherFoo.aBool) ...
    }
}

Como la especificación C# (secciones 3.5.1, 3.5.2) indica que el acceso a los campos privados es de un tipo, no de una instancia. He estado discutiendo esto con un colega y estamos tratando de llegar a una razón por la que funciona así (en lugar de restringir el acceso a la misma instancia).

El mejor argumento que se nos podría ocurrir es para verificaciones de igualdad donde la clase puede querer acceder a campos privados para determinar la igualdad con otra instancia. ¿Hay otras razones? ¿O alguna razón de oro que absolutamente significa que debe funcionar así o algo sería completamente imposible?

Author: AustinWBryan, 2011-08-08

10 answers

Creo que una razón por la que funciona de esta manera es porque los modificadores de acceso funcionan en tiempo de compilación. Como tal, determinar si un objeto dado es también el objeto actual no es fácil de hacer. Por ejemplo, considere este código:

public class Foo
{
    private int bar;

    public void Baz(Foo other)
    {
        other.bar = 2;
    }

    public void Boo()
    {
        Baz(this);
    }
}

¿Puede el compilador necesariamente darse cuenta de que other es en realidad this? No en todos los casos. Uno podría argumentar que esto simplemente no debería compilar entonces, pero eso significa que tenemos una ruta de código donde un miembro de instancia privada de la correcta instance no es accesible, lo que creo que es aún peor.

Solo requerir visibilidad a nivel de tipo en lugar de a nivel de objeto asegura que el problema sea tratable, así como hacer que una situación que parece debería funcionar en realidad funcionar.

EDITAR : El punto de Danilel Hilgarth de que este razonamiento es hacia atrás tiene mérito. Los diseñadores de lenguaje pueden crear el lenguaje que quieran, y los escritores del compilador deben ajustarse a él. Dicho esto, el lenguaje los diseñadores tienen algún incentivo para que sea más fácil para los escritores de compiladores hacer su trabajo. (Aunque en este caso, es bastante fácil argumentar que los miembros privados podrían entonces solo ser accedidos a través de this (ya sea implícita o explícitamente)).

Sin embargo, creo que eso hace que el tema sea más confuso de lo necesario. La mayoría de los usuarios (incluido yo mismo) lo encontrarían innecesariamente limitado si el código anterior no funcionara: después de todo, ¡esos son mis datos a los que estoy tratando de acceder! Por qué debería Tengo que pasar por this?

En resumen, creo que he exagerado el argumento de que es "difícil" para el compilador. Lo que realmente quería transmitir es que la situación anterior parece que a los diseñadores les gustaría tener trabajo.

 72
Author: dlev,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-08-09 12:53:01

Porque el propósito de el tipo de encapsulación utilizado en C# y lenguajes similares* es reducir la dependencia mutua de diferentes piezas de código (clases en C# y Java), no de diferentes objetos en memoria.

Por ejemplo, si escribe código en una clase que usa algunos campos en otra clase, entonces estas clases están muy estrechamente acopladas. Sin embargo, si está tratando con código en el que tiene dos objetos de la misma clase, entonces no hay dependencia adicional. Una clase siempre depende de sí mismo.

Sin embargo, toda esta teoría sobre la encapsulación falla tan pronto como alguien crea propiedades (o pares get/set en Java) y expone todos los campos directamente, lo que hace que las clases estén tan acopladas como si estuvieran accediendo a los campos de todos modos.

*Para una aclaración sobre los tipos de encapsulación ver la excelente respuesta de Abel.

 58
Author: Goran Jovic,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-08-11 15:38:28

Ya se han agregado bastantes respuestas a este interesante hilo, sin embargo, no encontré la verdadera razón de por qué este comportamiento es como es. Déjame intentarlo:

De vuelta en los días

En algún lugar entre Smalltalk en los 80 y Java a mediados de los 90, el concepto de orientación a objetos maduró. La ocultación de información, que originalmente no se pensó como un concepto solo disponible para OO (mencionado por primera vez en 1978), se introdujo en Smalltalk como todos los datos (campos) de una clase es privada, todos los métodos son públicos. Durante los muchos nuevos desarrollos de OO en los años 90, Bertrand Meyer trató de formalizar gran parte de los conceptos de OO en su libro de referencia Object Oriented Software Construction (OOSC) que desde entonces se ha considerado una referencia (casi) definitiva sobre los conceptos de OO y el diseño del lenguaje.

En el caso de la visibilidad privada

De acuerdo con Meyer, un método debe estar disponible para un conjunto definido de clases (página 192-193). Esto da obviamente una granularidad muy alta de ocultación de información, la siguiente característica está disponible para ClassA y ClassB y todos sus descendientes:

feature {classA, classB}
   methodName

En el caso de private dice lo siguiente: sin declarar explícitamente un tipo como visible para su propia clase, no se puede acceder a esa característica (método/campo) en una llamada calificada. Es decir, si x es una variable, x.doSomething() no está permitido. El acceso no cualificado está permitido, por supuesto, dentro de la propia clase.

En otros palabras: para permitir el acceso de una instancia de la misma clase, debe permitir el acceso del método por esa clase explícitamente. Esto a veces se llama instancia-privada versus clase-privada.

Instancia-privada en lenguajes de programación

Conozco al menos dos lenguajes actualmente en uso que usan ocultación de información privada de instancia en lugar de ocultación de información privada de clase. Uno es Eiffel, un lenguaje diseñado por Meyer, que lleva a OO a sus extremos. El otro es Ruby, un lenguaje mucho más común hoy en día. En Ruby, private significa: "privado para esta instancia".

Opciones para el diseño del lenguaje

Se ha sugerido que permitir instance-private sería difícil para el compilador. No lo creo, ya que es relativamente simple simplemente permitir o no permitir llamadas calificadas a métodos. Si para un método privado, doSomething() está permitido y x.doSomething() no, un diseñador de lenguaje ha definido efectivamente la accesibilidad de solo instancia para métodos privados y campo.

Desde un punto de vista técnico, no hay razón para elegir una forma u otra (esp. al considerar que Eiffel.NET puede hacer esto con IL, incluso con herencia múltiple, no hay ninguna razón inherente para no proporcionar esta característica).

Por supuesto, es una cuestión de gusto y como otros ya han mencionado, algunos métodos podrían ser más difíciles de escribir sin la característica de visibilidad a nivel de clase de los métodos y campos privados.

Por qué C # solo permite la clase encapsulación y no encapsulación de instancia

Si nos fijamos en los hilos de Internet en la encapsulación de instancias (un término que a veces se usa para referirse al hecho de que un lenguaje define los modificadores de acceso a nivel de instancia, en lugar de a nivel de clase), el concepto es a menudo mal visto. Sin embargo, teniendo en cuenta que algunos lenguajes modernos usan encapsulación de instancias, al menos para el modificador de acceso privado, te hace pensar que puede ser y es útil en el mundo de la programación moderna.

Sin embargo, Es cierto que C# ha mirado más duro a C++ y Java por su diseño de lenguaje. Mientras que Eiffel y Modula-3 también estaban en la imagen, considerando las muchas características de Eiffel missing (herencia múltiple) creo que eligieron la misma ruta que Java y C++ cuando se trataba del modificador de acceso privado.

Si realmente quieres saber el por qué deberías intentar contactar con Eric Lippert, Krzysztof Cwalina, Anders Hejlsberg o cualquier otra persona que haya trabajado en el estándar de C#. Desafortunadamente, no se pudo encontrar una nota definitiva en el anotado El Lenguaje de Programación C# .

 46
Author: Abel,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-08-10 16:31:53

Esta es solo mi opinión, pero pragmáticamente, creo que si un programador tiene acceso a la fuente de una clase, puede confiar razonablemente en ellos para acceder a los miembros privados de la instancia de la clase. ¿Por qué atar a un programador de la mano derecha cuando en su izquierda ya les has dado las llaves del reino?

 18
Author: FishBasketGordo,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-08-08 14:26:59

La razón de hecho es la comprobación de igualdad, comparación, clonación, sobrecarga del operador... Sería muy difícil implementar operator+ en números complejos, por ejemplo.

 13
Author: Lorenzo Dematté,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-08-08 14:27:57

En primer lugar, ¿qué pasaría con los miembros estáticos privados? ¿Solo se puede acceder a ellos por métodos estáticos? Ciertamente no querrías eso, porque entonces no podrías acceder a tus consts.

En cuanto a su pregunta explícita, considere el caso de un StringBuilder, que se implementa como una lista vinculada de instancias de sí mismo:

public class StringBuilder
{
    private string chunk;
    private StringBuilder nextChunk;
}

Si no puedes acceder a los miembros privados de otras instancias de tu propia clase, tienes que implementar ToString así:

public override string ToString()
{
    return chunk + nextChunk.ToString();
}

Esto hará trabajo, pero es O (n^2) not no muy eficiente. De hecho, eso probablemente frustra todo el propósito de tener una clase StringBuilder en primer lugar. Si puede acceder a los miembros privados de otras instancias de su propia clase, puede implementar ToString creando una cadena de la longitud adecuada y luego haciendo una copia insegura de cada fragmento en su lugar apropiado en la cadena:

public override string ToString()
{
    string ret = string.FastAllocateString(Length);
    StringBuilder next = this;

    unsafe
    {
        fixed (char *dest = ret)
            while (next != null)
            {
                fixed (char *src = next.chunk)
                    string.wstrcpy(dest, src, next.chunk.Length);
                next = next.nextChunk;
            }
    }
    return ret;
}

Esta implementación es O (n), lo que la hace muy rápida, y es solo posible si tiene acceso a miembros privados de otras instancias de su clase.

 9
Author: Gabe,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-08-08 17:51:12

Esto es perfectamente legítimo en muchos lenguajes (C++ para uno). Los modificadores de acceso provienen del principio de encapsulación en OOP. La idea es restringir el acceso a fuera de , en este caso fuera hay otras clases. Cualquier clase anidada en C#, por ejemplo, también puede acceder a los miembros privados de sus padres.

Mientras que esta es una opción de diseño para un diseñador de lenguaje. La restricción de este acceso puede complicar en extremo algunos escenarios muy comunes sin contribuir mucho al aislamiento de entidades.

Hay una discusión similar aquí

 3
Author: Mehran,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-05-23 12:10:44

No creo que haya una razón por la que no podamos agregar otro nivel de privacidad, donde los datos son privados para cada instancia. De hecho, eso podría incluso proporcionar una agradable sensación de integridad al idioma.

Pero en la práctica real, dudo que realmente sea tan útil. Como ha señalado, nuestra privacidad habitual es útil para cosas como comprobaciones de igualdad, así como la mayoría de otras operaciones que involucran múltiples instancias de un Tipo. Aunque, también me gusta su punto sobre mantener abstracción de datos, ya que es un punto importante en OOP.

Creo que en general, proporcionar la capacidad de restringir el acceso de tal manera podría ser una buena característica para agregar a OOP. ¿Es realmente tan útil? Yo diría que no, ya que una clase debería ser capaz de confiar en su propio código. Dado que esa clase es lo único que puede acceder a miembros privados, no hay ninguna razón real para necesitar abstracción de datos cuando se trata de una instancia de otra clase.

Por supuesto, siempre puede escribir su código como si private se aplica a las instancias. Utilice los métodos habituales get/set para acceder/cambiar los datos. Eso probablemente haría que el código sea más manejable si la clase puede estar sujeta a cambios internos.

 1
Author: Ken Wayne VanderLinde,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-08-08 14:44:09

Grandes respuestas dadas arriba. Yo añadiría que parte de este problema es el hecho de que la instanciación de una clase dentro de sí misma incluso está permitida en primer lugar. Hace que en un bucle de lógica recursiva "for", por ejemplo, se use ese tipo de truco siempre y cuando se tenga lógica para finalizar la recursión. Pero instanciar o pasar la misma clase dentro de sí misma sin crear tales bucles crea lógicamente sus propios peligros, a pesar de que es un paradigma de programación ampliamente aceptado. Por ejemplo, una clase C# puede instanciar una copia de sí mismo en su constructor predeterminado, pero eso no rompe ninguna regla ni crea bucles causales. ¿Por qué?

POR cierto....este mismo problema se aplica también a los miembros" protegidos". :(

Nunca acepté ese paradigma de programación completamente porque todavía viene con un conjunto completo de problemas y riesgos que la mayoría de los programadores no comprenden completamente hasta que surgen problemas como este problema y confunden a la gente y desafían toda la razón de tener miembros privados.

Este "raro y loco " aspecto de C# es sin embargo una razón más por qué una buena programación no tiene nada que ver con la experiencia y la habilidad, pero solo conocer los trucos y trampas.....como trabajar en un auto. Es el argumento de que las reglas estaban destinadas a romperse, lo que es un modelo muy malo para cualquier lenguaje de computación.

 0
Author: Stokely,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-08-31 15:15:21

Me parece que si los datos fueran privados para otras instancias del mismo tipo, ya no necesariamente serían del mismo tipo. No parece comportarse o actuar de la misma manera que otras instancias. El comportamiento podría modificarse fácilmente en función de esos datos internos privados. Eso sólo generaría confusión en mi opinión.

En términos generales, personalmente creo que escribir clases derivadas de una clase base ofrece una funcionalidad similar a la que está describiendo con ' tener datos privados por instance". En su lugar, solo tiene una nueva definición de clase por tipo 'único'.

 -1
Author: C Johnson,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-08-08 14:33:18