Reflection是否打破了封装原则?

好吧,假设我们有一个类定义的类

public class TestClass { private string MyPrivateProperty { get; set; } // This is for testing purposes public string GetMyProperty() { return MyPrivateProperty; } } 

然后我们尝试:

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

编译失败, TestClass.MyPrivateProperty is inaccessible due to its protection level ,正如预期的那样。

尝试

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

并使用相同的消息再次编译失败。

一切都很好,直到现在,我们都期待着这一点。

但后来写道:

 PropertyInfo aProp = t.GetType().GetProperty( "MyPrivateProperty", BindingFlags.NonPublic | BindingFlags.Instance); // This works: aProp.SetValue(t, "test", null); // Check Console.WriteLine(t.GetMyProperty()); 

在这里,我们设法改变了一个私人领域。

仅使用reflection能够改变某个物体的内部状态是不是不正常?

编辑:

感谢到目前为止的回复。 对于那些说“你不必使用它”的人:那个class级设计师怎么样,看起来他再也不能承担内部的国家安全了?

reflection通过提供对私有字段和方法的访问来打破封装原则,但它不是第一种或唯一可以绕过封装的方式; 有人可能会争辩说序列化会暴露一个类的所有内部数据,这些信息通常是私有的。

重要的是要理解封装只是一种技术,如果消费者同意使用您定义的API,则可以使设计行为更容易。 如果有人选择使用reflection或任何其他技术绕过您的API,他们就不再能保证您的对象将按照您的设计行事。 如果有人为私有字段赋值null ,那么下次他们尝试使用你的类时,他们最好准备好捕获NullReferenceException

根据我的经验,编程是关于断言和假设的。 语言断言约束(类,接口,枚举),这使得创建隔离行为更容易生成,假设消费者同意不违反这些边界。

这是一个公平的断言 ,因为它比以前的任何技术都更容易实现软件开发的分而治之的方法。

反思是一种工具 。 你可以用它来打破封装,当它给你带来的东西比它带走的更多。

reflection具有与之相关的某种“痛苦”(或成本 – 性能,可读性,代码可靠性),因此您不会将其用于常见问题。 对于常见问题,遵循面向对象原则更容易 ,这是该语言的目标之一,通常被称为成功的关键 。

另一方面,有一些任务在没有这种机制的情况下是无法解决的,例如使用运行时生成类型(但是,从.NET 4.0开始,它的DLR和“它将会更加容易”。 C#4.0中的动态“变量”。

我想你可以这么说。 您还可以说CodeDom,Reflection Emit和Expression Tree API允许您动态编写代码来破坏封装。 它们仅仅是.NET提供的工具。 您不必使用它们。

不要忘记您必须(a)以完全信任的方式运行,并且(b)明确指定BindingFlags.NonPublic ,以便访问私有数据。 这应该足够安全网了。

你是对的,reflection可以与任何数量的优秀设计原则相对立,但它也可以是一个必不可少的构建块,可以用来支持良好的设计原则 – 例如可以通过插件扩展的软件,控制反转等。

如果你担心它代表了一种不应该被劝阻的能力,那么你可能会有所帮助。 但它不像真正的语言function那样方便,因此以正确的方式做事更容易。

如果你认为反思应该是不可能的 ,你就是在做梦!

在C ++中没有这样的reflection。 但是有一个底层的“对象模型”,编译器生成的机器代码用它来访问对象(和虚函数)的结构。 因此,C ++程序员可以以相同的方式打破封装。

 class RealClass { private: int m_secret; }; class FakeClass { public: int m_notSecret; }; 

我们可以指向RealClass类型的对象,只需将其转换为FakeClass并访问“私有”成员。

任何受限制的系统都必须在更灵活的系统之上实现,因此始终可以绕过它。 如果没有提供reflection作为BCLfunction,有人可以使用不安全的代码将其添加到库中。

在某些语言中,有一些方法可以封装数据,因此除了某些规定的方式之外, 语言中不可能获取数据。 但是,如果你能找到逃避语言的方法,那么总是可以作弊。 一个极端的例子是JavaScript中的作用域变量:

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

执行完毕后,全局命名空间包含两个函数myGettermySetter ,它们是访问x值的唯一方法。 Javascript没有reflection能力以任何其他方式获得x 。 但它必须在某种主机解释器中运行(例如在浏览器中),因此操作x肯定有一些可怕的方法。 插件中的内存损坏错误可能会偶然发生!

可以说reflection也会破坏inheritance和多态性。 当不是为每个对象类型提供自己的重载方法版本时,您有一个方法可以在运行时检查对象类型并切换到每个对象类型的特定行为。

尽管如此,reflection是一个很好的工具。 有时您需要使用私有构造函数实例化一个对象,因为它是一个最佳的操作过程,您无法更改类实现(封闭库,无法再与作者联系)。 或者您希望在一个地方对各种物体执行一些辅助操作。 或者您可能希望对某些类型执行日志记录。 当reflection确实有助于不造成任何伤害时,就是这种情况。

这是Reflection提供的function的一部分,但不是,我会说,最好的使用它。

它只能在完全信任下运作。

封装原则由您的API保留,但reflection为您提供了一种解决API的方法。

使用reflection的人都知道他没有正确使用你的API!

不,它没有exception。
reflection允许你这样做,在某些情况下你所做的可能是有用的,在许多其他情况下,只会使你的代码成为定时炸弹。

反思是一种工具,它可以被使用或滥用。

关于编辑后的问题:答案是否定的。 API设计人员可以尽力而为,为暴露一个干净的界面付出很多努力,并通过使用reflection来看他的API过度滥用。

一个工程师可以建造一个安全的完美洗衣机,不会给你带来电击等等,但是如果你用锤子砸它,直到看到电缆,如果你受到冲击,没有人会责怪工程师: d

reflection可以帮助您保持代码清洁。 例如,当你使用hibernate时,你可以直接将私有变量绑定到DB值,而不必编写你不需要的不必要的setter方法。 从这个角度来看,reflection可以帮助你保持封装。