【问题标题】:Is Reflection breaking the encapsulation principle?反射是否打破了封装原则?
【发布时间】:2010-12-20 05:51:28
【问题描述】:

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

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());

在这里,我们设法更改了一个私有字段。

仅仅通过反射就可以改变某个对象的内部状态是不是很不正常?

编辑:

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

【问题讨论】:

  • 仅仅使用反射?有人不只是使用反射,他确切地知道,他正在破坏 API。

标签: c# oop reflection


【解决方案1】:

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

重要的是要理解封装只是一种技术,它可以让设计行为变得更容易,前提是消费者同意使用您定义的 API。如果有人选择使用反射或任何其他技术来规避您的 API,他们将不再保证您的对象会按照您设计的方式运行。如果有人将null 的值分配给私有字段,那么他们最好准备好在下次尝试使用您的类时捕获NullReferenceException

根据我的经验,编程就是断言和假设。该语言声明了约束(类、接口、枚举),这使得创建孤立的行为更容易产生,假设消费者同意不违反这些边界。

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

【讨论】:

  • 虽然这个问题得到了其他很好的答案,但我之所以选择这个,是因为强调人们应该将封装视为一种(推荐的)技术。
【解决方案2】:

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

反射有一定的“痛苦”(或成本——在性能、可读性、代码可靠性方面),因此您不会将它用于常见问题。 更容易遵循面向对象的原则来解决常见问题,这是该语言的目标之一,通常称为the pit of success

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

【讨论】:

    【解决方案3】:

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

    如果您担心它代表了一种不应该被阻止的能力,那么您可能有道理。但是使用起来不如真正的语言特性方便,所以用对的方式做事更容易。

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

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

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

    我们可以将一个指向RealClass 类型对象的指针简单地转换为FakeClass 并访问“私有”成员。

    任何受限系统都必须在更灵活的系统之上实施,因此始终可以绕过它。如果没有将反射作为 BCL 功能提供,那么有人可以使用不安全代码将其与库一起添加。

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

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

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

    【讨论】:

      【解决方案4】:

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

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


      关于您编辑后的问题:答案是否定的。 API 设计人员可以尽其所能,投入大量精力来公开一个干净的接口,并看到他的 API 被反射过度滥用。

      工程师可以为完美的洗衣机建模,该洗衣机既安全又不会电击等,但如果你用锤子砸它直到看到电缆,如果你得到了,没人会责怪工程师震惊:D

      【讨论】:

      • 哈哈,洗衣机...所以封装附带保修证书,你是说;)?
      • 嘿嘿嘿...也许有合同:P
      【解决方案5】:

      反射可以帮助您保持代码整洁。例如。当您使用休眠时,您可以直接将私有变量绑定到 DB 值,并且您不必编写不必要的 setter 方法,否则您将不需要这些方法。从这个角度来看,反射可以帮助你保持封装。

      【讨论】:

        【解决方案6】:

        您的 API 遵循封装原则,但反射为您提供了一种绕过 API 的方法。

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

        【讨论】:

          【解决方案7】:

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

          不要忘记,您必须 (a) 在完全信任的情况下运行,并且 (b) 明确指定 BindingFlags.NonPublic,才能访问私有数据。这应该是足够的安全网。

          【讨论】:

            【解决方案8】:

            这是 Reflection 提供的功能的一部分,但我想说,它不是最好的使用方法。

            它只在完全信任下工作。

            【讨论】:

              【解决方案9】:

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

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

              【讨论】:

                猜你喜欢
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 2021-12-15
                • 1970-01-01
                • 1970-01-01
                • 2014-10-12
                • 2010-10-10
                相关资源
                最近更新 更多