【问题标题】:Why is it even possible to change a private member, or run a private method in C# using reflection? [duplicate]为什么甚至可以更改私有成员,或使用反射在 C# 中运行私有方法? [复制]
【发布时间】:2013-06-05 14:06:36
【问题描述】:

我最近在使用 C# 时遇到了一个问题,通过使用 reflection 设置私有成员解决了这个问题。

我惊讶地发现设置私有成员/字段并运行私有方法是 C# 中允许和可能的事情。这不是如何做这些事情的问题,它们有据可查,我的问题是:为什么?

如果您将字段/成员/方法设置为私有/内部,为什么 C# 作为一种语言允许在范围之外设置这些字段?我认为这会引发某种异常。如果类希望它们被更改或设置,不会有方法或构造函数吗?

【问题讨论】:

  • 一旦你使用反射、手动发送字节码等,你会说'填充普通的面向对象模型,我需要这样做,我现在需要它'。这意味着取消了正常的限制,因为您明确表示您想要绕过它们。权力越大,责任越大。
  • 只是为了迂腐,C# 语言不允许这样做。 System.Reflection 命名空间中的类不是 C# 特定的。
  • 如果调试器无法访问您正在调试的类的私有成员,它们将不会很有用。
  • 顺便说一句,考虑一下这个小 sn-p 的乐趣:typeof(String).GetField("Empty", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.GetField)。 SetValue(null, "foo");

标签: c# .net reflection


【解决方案1】:

因为访问修饰符有助于记录您希望向消费者或继承者等公开的 API。

它们不是一种安全/访问控制机制。

【讨论】:

  • 我认为关键字 unsafe 的存在意味着即使反射不存在,您也可以访问 C# 数据类型的私有字段。 (我不是要尝试的游戏)
  • @Patashu 即使它不存在,您仍然可以调用例如 C++ 代码,这些代码可以随意在进程内存中的任何地方转来转去。归根结底,您无法阻止程序操纵自己的内存空间。为了能够正常运行,它需要有足够的访问权限来使用该内存执行几乎任何它想做的事情。
  • 我不认为说访问修饰符不是安全访问控制机制是完全公平的,尽管它们不提供 OP 讨论的类型的安全性。见Do the access levels and modifiers (private, sealed, etc) serve a security purpose in C#?。 Per Eric Lippert:“访问限制减轻了部分受信任的恶意代码对用户的攻击。”
【解决方案2】:

不可能阻止某人这样做。你可以让它变得更难,你可以强迫他们使用不安全的代码并开始盲目地设置位。毕竟,这是他们的程序/机器,他们被允许做这样的事情。

这种语言的设计让你很难自责和做坏事。但这并不会使它们成为不可能,这样做还会限制用户做一些不寻常但仍可取的事情。

【讨论】:

  • 这是我最喜欢的答案。修改值只是一种访问内存的机制——因此存在巨大的病毒预防/清除市场。这个想法是,如果你让消费者不那么可见,他们会意识到一般而言这就是界面应该使用的方式。这也意味着通过Reflection修改数据可以导致不稳定(虽然我已经做到了):D
  • 这不是不可能的。简直太难了。例如,您可以运行一个程序,以便拦截每个内存访问(使用 mmu)并通过单独的进程(使用调试 API)或通过内核或某种类型的 vm 监视器进行验证。这样的监控程序可以内省内存访问并做出非常复杂的策略决策。这会非常慢,但有可能。
  • @ScottWisniewski 但是,如果有人还篡改了执行访问策略的软件的内存,从而使他们能够访问以修改其他进程,该怎么办? :p
【解决方案3】:

私人反思要求您获得基本上完全的信任。完全信任意味着完全信任。如果您完全相信自己会做正确的事,那为什么不这样做呢?

(事实上,私有反射的安全模型实际上比我在这里描述的要复杂得多,但这并不影响我的观点:这种能力受制于政策。见Security Considerations for Reflection (MSDN) 了解反射和安全策略如何交互的概述。)

【讨论】:

  • 现在这是我见过的最清晰的私人反思。这需要完全信任,谢谢 Eric!
  • @Michael 实际上在实践中要复杂得多。为了说明这一点,我过于简单化了;此功能由策略控制。
  • 虽然controlled by a policy 的评论中的小金块也很大 - 这很有意义 - 虽然我还不完全理解它,但它让我知道网络管理员也可能控制该策略所以我不应该假设我总是可以访问私人反射。再次,必须做一些阅读,但那是一个伟大的掘金。非常感谢!
【解决方案4】:

可见性修饰符并不意味着安全。

它们只是为了更有效地使用结构化 API/代码库 - 以免程序员因胡言乱语而陷入困境 - 并且只公开消费者应该关注的事情。

【讨论】:

    【解决方案5】:

    反射是您与已编译代码交互的方式。如果反射尊重源语言对隐私的期望,那么就需要另一种机制,就像它没有的那样。乌龟一路下来。

    首先假设它所做的是它的本意,然后重新评估你的假设,即做一些没有人想做的不同的事情是一种错误的努力。

    【讨论】:

      【解决方案6】:

      有时候,你必须作弊。

      很容易说,如果需要设置,类应该有一个设置器,但如果你不能添加一个怎么办?如果它不是您的类,而是一个专有库,您需要解决其中的错误怎么办?我并不是说你应该做这样的事情,事实上我会说你几乎肯定不应该这样做,但有时让某些事情发挥作用的唯一方法是无耻的作弊。在这些情况下,这些机制的存在是非常有帮助的。

      但是,您应该始终质疑它们的使用,当然要注意,违反对象的公共 API 可能会导致您进一步出现问题,并且可能是错误的做。除非在上述情况下,只有这样才能使某些代码在您无法更改的代码中运行。

      需要注意的一点是,C++ 及其衍生版本具有强大的访问控制能力,但也有很多 OOP 语言没有。例如,Perl 5 对象对外部干扰完全开放,私有方法和数据只是按照惯例存在 - Perl 程序员知道在第三方对象的 hashref 内部搞乱可能会破坏事情,所以他们通常会赢不要那样做。再次与 C# 的反射功能一样,有时您可以通过对您不应该触及的东西进行一点调整来解决大量问题。

      但我再次重申,您几乎可以肯定不应该这样做。这些功能不适合日常使用。

      【讨论】:

        【解决方案7】:

        如果没有反光的东西,我什至看不到自己的脸。好吧,我可以在视频中观看自己,但这是由称为相机的光学设备完成的,它也是使用反射技术的东西。

        如果我生病了,当医生需要X光来诊断和治疗我的病时,我可能已经死了,但各种反思都无法看到我的隐私。我也永远看不到自己。

        X 射线(或类似 tomography 的东西)可以观察我的内心,没有它我永远做不到。

        我宁愿说它比反思更现实。但是,是的,我不能用我的眼睛直接看到真实的我,我所见过的每一个我,都是某种反映。 (深入思考,眼睛也给了我们现实的反映。)

        因此,反思应该与现实有关,没有特定的观点。并且您可以假设在面向对象的规则内,消费者代码仅限于 BindingFlags.Public。

        在真实的宇宙中,几乎没有什么是不可能的;对我们来说,可能和不可能之间的唯一区别是它是否可以由人类完成,因为我们是人类。

        反射可以完成程序世界中的所有事情似乎很危险,现在出于安全原因,按照人类的逻辑,它需要被完全信任。

        【讨论】:

          【解决方案8】:

          好问题。

          我的回答是:访问修饰符和可见性是 API 设计的一部分。通过反射,您可以完全控制几乎所有内容,包括绕过 API 和在底层修改数据。

          访问修饰符可以使您的类免受外部意外更改的影响。您可以“偶然”调用方法或访问公共成员。有了反思,就很难说你做事是偶然的。

          如果这种基于反射的低级访问是不可能的,那么我们都知道和喜欢的很多东西几乎是不可能的。

          【讨论】:

            【解决方案9】:

            生产代码中的一个案例:

            我们希望一项网络服务能够在新西兰时区工作。可能正确的解决方案是重写我们所有的代码(包括一些 .NET Framework 序列化代码)以使用 DateTimeOffset,但最简单的解决方案是有效地调整 .NET Framework 中存储当前时区的两个私有字段(通常基于在注册调用时)明确使用新西兰时区。

            我们知道,如果 .NET Framework 2.0 版在其时区处理方面有所更新,我们可能不得不重新编写我们的代码,但目前这在生产环境中运行良好。

            【讨论】:

              猜你喜欢
              • 2011-01-06
              • 2010-12-21
              • 2011-05-13
              • 1970-01-01
              • 2011-11-11
              • 2014-04-02
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              相关资源
              最近更新 更多