C# 反射是否违反了封装原则?

C# 反射是否违反了封装原则?,c#,oop,reflection,C#,Oop,Reflection,好吧,假设我们有一个定义如下的类 public class TestClass { private string MyPrivateProperty { get; set; } // This is for testing purposes public string GetMyProperty() { return MyPrivateProperty; } } 然后我们尝试: TestClass t = new TestClass {

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

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由于其保护级别而无法访问,如预期的那样

试一试

编译再次失败,并显示相同的消息

到目前为止一切都很好,我们都在期待着

但有人写道:

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

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

// Check
Console.WriteLine(t.GetMyProperty());
现在,我们设法改变了一个私人领域

仅仅通过反射就能改变某个物体的内部状态,这不是很反常吗

编辑:


谢谢你到目前为止的回复。对于那些说“你不必使用它”的人:对于一个类设计者来说,看起来他不能再假设内部状态安全了吗?

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

反射有一定的“痛苦”(或成本——在性能、可读性和代码可靠性方面)与之相关,所以您不会将其用于常见问题。对于常见的问题,遵循面向对象的原则更容易,这是该语言的目标之一,通常称为


另一方面,如果没有这种机制,有些任务是无法解决的,例如,使用运行时生成类型(不过,从.NET 4.0开始使用DLR和C#4.0中的“动态”变量会容易得多)。

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


然而,反射是一个很好的工具。有时,您需要使用私有构造函数实例化对象,因为这是一个最佳的操作过程,并且您无法更改类实现(封闭库,无法再与作者联系)。或者您希望在一个位置对各种对象执行一些辅助操作。或者,您可能希望对某些类型执行日志记录。这些情况下,反射确实有帮助,但不会造成任何伤害。

这是反射提供的功能的一部分,但我想说的是,这不是它的最佳使用


它只能在完全信任的情况下工作。

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


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

不,这不是异常。
这是反射允许你做的事情,在某些情况下,你所做的可能会有一些用处,在许多其他情况下,这将使你的代码成为一颗定时炸弹

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


关于编辑后的问题:答案是否定的。 API设计师可以尽最大努力,将大量精力投入到公开干净的接口上,并通过使用反射看到自己的API被过度滥用


工程师可以模拟一台完美的洗衣机,它安全可靠,不会让你触电,但是如果你用锤子砸碎它直到看到电缆,如果你受到电击,没有人会责怪工程师:D

封装原理由你的API决定,但反射为你提供了一种绕过API的方法


使用反射的人都知道他没有正确使用您的API

反射可以帮助您保持代码干净。例如,当您使用hibernate时,您可以直接将私有变量绑定到DB值,而不必编写其他情况下不需要的不必要的setter方法。从这个角度来看,反射可以帮助您保持封装。

您是对的,反射可以反对任何数量的好设计原则,但它也可以是一个基本的构建块,您可以使用它来支持好的设计原则,例如,可以通过插件扩展的软件、控制反转等

如果您担心它代表了一种不应该被鼓励的能力,那么您可能有道理。但它不像真正的语言功能那样方便使用,所以更容易用正确的方式做事

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

在C++中没有这样的反射。但编译器生成的机器代码使用一个底层“对象模型”来访问对象(和虚拟函数)的结构。因此,C++程序员可以以同样的方式破坏封装。p>
class RealClass
{
private:
    int m_secret;
};

class FakeClass
{
public:
    int m_notSecret;
};
我们可以获取一个指向类型为
RealClass
的对象的指针,然后简单地将其强制转换为
FakeClass
并访问“private”成员

任何受限系统都必须在更灵活的系统之上实施,因此总是有可能绕过它。如果反射不是作为BCL特性提供的,则有人可以使用不安全代码将其添加到库中

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

(function() {

    var x = 5;

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

})();
执行后,全局名称空间包含两个函数,
myGetter
mySetter
,这是访问
x
值的唯一方法。Javascript没有反射能力以任何其他方式获取
x
。但是它必须在某种主机解释器中运行(例如在浏览器中),因此肯定有一些可怕的方式来操纵
x
。插件中的内存损坏错误可能会意外地造成这种情况

R
(function() {

    var x = 5;

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

})();