为什么甚至可以更改私有成员,或者使用反射在C#中运行私有方法?

时间:2013-06-05 14:06:36

标签: c# .net reflection

我最近遇到了一个问题,即我使用的是C#,它是通过使用reflection设置私有成员来解决的。

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

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

9 个答案:

答案 0 :(得分:133)

因为访问修饰符可以帮助记录您想要向消费者或继承者等公开的API。

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

答案 1 :(得分:64)

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

这种语言的设计使你很难在脚下拍摄并做坏事。但这并不能使它们变得不可能,这样做也会限制用户做一些不寻常但仍然可取的事情。

答案 2 :(得分:56)

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

(事实上,私人反思的安全模型实际上比我在这里描绘的要复杂得多,但这并不影响我的观点:这种能力受政策的约束。 Security Considerations for Reflection (MSDN),概述了反射和安全策略的交互方式。)

答案 3 :(得分:14)

可见性修饰符不是为了安全。

它们只是为了使结构化API /代码库的使用更加有效 - 不会让程序员陷入困境 - 并且只暴露消费者应该关注的事物。

答案 4 :(得分:6)

反思是你如何与编译的代码进行交互。如果反思尊重源语言对隐私的期望,那么就需要另一种机制,就像它没有那样。海龟一路下来。

首先假设它的作用是它的意图,然后重新评估你的假设,即做一些不同的事情是一种误导的努力,无论如何都不想做。

答案 5 :(得分:5)

有时,你必须作弊。

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

然而,你应该始终质疑它们的使用,当然要知道违反对象的公共API可能会导致你的问题进一步发生,并且可能是错误的做。除了前面提到的情况,这是你可以在某些代码中使某些东西无法改变的唯一方法。

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

但我再说一次,你几乎肯定不应该这样做。这些功能不适合日常使用。

答案 6 :(得分:5)

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

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

X射线(或类似tomography之类的东西)可以看到我的内心,没有它我永远无法做到。

1kUml.jpg

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

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

在现实世界中,几乎没有什么是不可能的;我们可能和不可能之间的唯一区别在于它是否可以由人类完成,因为我们是人类。

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

答案 7 :(得分:3)

好问题。

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

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

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

答案 8 :(得分:0)

生产代码中的案例:

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

我们知道,如果.NET Framework 2.0版在处理时区时得到更新,我们可能需要重新编写代码,但目前这在生产中运行良好。