2009-11-25 24 views
8

bien, digamos que hemos definido como una clase¿La reflexión rompe el principio de encapsulación?

public class TestClass 
{ 
    private string MyPrivateProperty { get; set; } 

    // This is for testing purposes 
    public string GetMyProperty() 
    { 
     return MyPrivateProperty; 
    } 
} 

después tratamos:

TestClass t = new TestClass { MyPrivateProperty = "test" }; 

compilación falla con TestClass.MyPrivateProperty is inaccessible due to its protection level, como se esperaba.

Trate

TestClass t = new TestClass(); 
t.MyPrivateProperty = "test"; 

y compilación falla otra vez con el mismo mensaje.

Todo bien hasta ahora, esperábamos esto.

Pero entonces uno de escritura:

PropertyInfo aProp = t.GetType().GetProperty(
     "MyPrivateProperty", 
     BindingFlags.NonPublic | BindingFlags.Instance); 

// This works: 
aProp.SetValue(t, "test", null); 

// Check 
Console.WriteLine(t.GetMyProperty()); 

y aquí estamos, que logró cambiar un campo privado.

¿No es anormal poder alterar el estado interno de algunos objetos simplemente mediante el reflejo?

Editar:

Gracias por las respuestas hasta ahora. Para aquellos que dicen "no tienes que usarlo": ¿qué hay de un diseñador de clases, parece que ya no puede asumir la seguridad interna del estado?

+0

¿Solo usando el reflejo? Alguien no solo usa el reflejo, el sabe exactamente, que está rompiendo el API. –

Respuesta

13

La reflexión rompe los principios de encapsulación al dar acceso a campos y métodos privados, pero no es la primera o la única forma en que se puede eludir la encapsulación; se podría argumentar que la serialización expone todos los datos internos de una clase, información que normalmente sería privada.

Es importante entender que la encapsulación es solo una técnica, una que facilita el diseño del comportamiento, siempre que los consumidores acepten utilizar una API que usted haya definido. Si alguien elige eludir su API utilizando la reflexión o cualquier otra técnica, ya no tendrá la seguridad de que su objeto se comportará como lo diseñó. Si alguien asigna un valor de null a un campo privado, será mejor que estén listos para atrapar un NullReferenceException la próxima vez que intenten usar su clase.

En mi experiencia, la programación tiene que ver con aserciones y suposiciones. El lenguaje impone restricciones (clases, interfaces, enumeraciones) que facilitan la creación de comportamientos aislados, en el supuesto de que el consumidor acepte no violar esos límites.

Esta es una afirmación justa dar a dado hace un enfoque de divide y vencerás para el desarrollo de software más fácil que cualquier técnica antes.

+1

Aunque esta pregunta recibió otras excelentes respuestas, estoy eligiendo esto debido al énfasis puesto en el hecho de que uno debe considerar la encapsulación como una técnica (recomendada) entre muchas otras. –

8

Reflection es una herramienta. Usted puede usarlo para romper la encapsulación, cuando le da más de lo que le quita.

La reflexión tiene un cierto "dolor" (o costo - en el rendimiento, en la legibilidad, en la fiabilidad del código) asociado a ella, por lo que no lo usará para un problema común. Es solo más fácil seguir principios orientados a objetos para problemas comunes, que es uno de los objetivos del lenguaje, comúnmente conocido como the pit of success.

Por otro lado, hay algunas tareas que no se podrían resolver sin ese mecanismo, p. trabajar con tipos de generación en tiempo de ejecución (aunque será mucho más fácil comenzar desde .NET 4.0 con su DLR y las variables "dinámicas" en C# 4.0).

2

Se puede decir que la reflexión también rompe la herencia y el polimorfismo. Cuando en lugar de proporcionar a cada tipo de objeto su propia versión de método sobrecargado, tiene un único método que comprueba el tipo de objeto en tiempo de ejecución y cambia a un comportamiento particular para cada uno.

Sin embargo, la reflexión es una buena herramienta. A veces necesita crear instancias de un objeto con un constructor privado porque es un curso de acción óptimo y no puede cambiar la implementación de la clase (biblioteca cerrada, ya no puede ponerse en contacto con el autor). O desea realizar alguna operación auxiliar en una variedad de objetos en un solo lugar. O tal vez desee realizar el registro para ciertos tipos. Esta es la situación en que la reflexión ayuda sin causar ningún daño.

2

Esa es parte de la funcionalidad proporcionada por Reflection, pero no, diría, el mejor uso de la misma.

Solo funciona bajo la plena confianza.

3

Supongo que podría decir eso. También podría decir que las API CodeDom, Reflection Emit y Expression Tree rompen la encapsulación permitiéndole escribir código sobre la marcha. Son simplemente instalaciones proporcionadas por .NET. No tienes que usarlos.

No olvide que debe (a) ejecutar con plena confianza, y (b) especificar explícitamente BindingFlags.NonPublic, para acceder a datos privados. Eso debería ser suficiente de una red de seguridad.

2

No, no es anormal.
Es algo que el reflejo te permite hacer, en alguna situación lo que hiciste podría ser de algún uso, en muchos otros que simplemente convertirán tu código en una bomba de tiempo.

Reflection es una herramienta, se puede utilizar o abusar.


Sobre la pregunta después de su edición: la respuesta es simplemente no. Un diseñador de API podría hacer todo lo posible, poniendo mucho esfuerzo en exponer una interfaz limpia y ver su API excesivamente mal utilizada mediante el uso de la reflexión.

Un ingeniero puede modelar una lavadora perfecta que es segura, no lo electrocuta con electricidad y demás, pero si la golpea con un martillo hasta que vea los cables, nadie va a culpar al ingeniero si consiga un shock: D

+0

Haha, lavadora ... Entonces, la encapsulación viene con un certificado de garantía, ¿está diciendo?)? –

+0

Hehehe ... tal vez con un contrato: P –

2

El principio de encapsulación lo retiene su API, pero la reflexión le proporciona una forma de evitar la API.

¡Quien usa la reflexión sabe que no está usando su API correctamente!

+0

Muy claro y fácil de recordar:) ... –

1

Reflection puede ayudarlo a mantener su código limpio. P.ej. Cuando utiliza hibernación, puede vincular directamente las variables privadas a los valores de base de datos y no tiene que escribir métodos setter innecesarios que de otra manera no necesitaría. Visto desde este punto de vista, la reflexión puede ayudarlo a mantener la encapsulación.

3

Tiene razón en que la reflexión puede oponerse a cualquier número de buenos principios de diseño, pero también puede ser un elemento esencial que puede utilizar para respaldar los buenos principios de diseño, p. software que puede ampliarse mediante complementos, inversión de control, etc.

Si le preocupa que represente una capacidad que debe desaconsejarse, puede tener un punto. Pero no es tan conveniente usar como características del lenguaje verdadero, por lo que es más fácil hacer las cosas de la manera correcta.

Si piensas que la reflexión debería ser imposible, ¡estás soñando!

En C++ no hay reflexión como tal. Pero existe un "modelo de objetos" subyacente que el código máquina generado por el compilador usa para acceder a la estructura de los objetos (y funciones virtuales). Entonces, un programador de C++ puede romper la encapsulación de la misma manera.

class RealClass 
{ 
private: 
    int m_secret; 
}; 

class FakeClass 
{ 
public: 
    int m_notSecret; 
}; 

Podemos tomar un puntero a un objeto de tipo RealClass y simplemente echarlo a FakeClass y acceder al miembro de "privado".

Cualquier sistema restringido debe implementarse sobre un sistema más flexible, por lo que siempre es posible eludirlo. Si no se proporcionó la reflexión como una función BCL, alguien podría agregarla a una biblioteca con un código no seguro.

En algunos idiomas hay formas de encapsular datos para que no sea posible en el idioma para obtener los datos excepto en ciertas formas prescritas. Pero siempre será posible hacer trampa si puedes encontrar una forma de escapar del idioma. Un ejemplo extremo sería de ámbito de variables en JavaScript:

(function() { 

    var x = 5; 

    myGetter = function() { return x; }; 
    mySetter = function(v) { x = v; }; 

})(); 

Después de que se ejecuta, el espacio de nombres global contiene dos funciones, myGetter y mySetter, que son la única manera de acceder al valor de x. Javascript no tiene capacidad reflexiva para obtener x de otra manera. Pero tiene que ejecutarse en algún tipo de intérprete de host (por ejemplo, en el navegador), por lo que sin duda hay una forma horrible de manipular x. ¡Un error de corrupción de memoria en un complemento podría hacerlo por accidente!

Cuestiones relacionadas