在C#(和一般的OOP)中存在私有方法存在的任何充分理由吗?

时间:2012-07-05 02:11:00

标签: c# oop

我不是故意拖车,但我真的不明白。为什么语言设计者会允许私有方法而不是某些命名约定(参见Python中的__)? 我搜索了答案,通常的论点是:
a)使实现更清洁/避免在IDE自动完成中使用长的垂直方法列表 b)向全世界宣布哪些方法是公共接口,哪些方法可能会发生变化,仅供实施之用 c)可读性

好的,现在所有这些都可以通过使用__前缀或“private”关键字命名所有私有方法来实现,除了是IDE的信息之外没有任何其他含义(不要将它们放在自动完成中)和其他程序员(除非你真的必须使用它,否则不要使用它)。地狱,人们甚至可以要求不安全的关键字访问私人方法,以真正阻止这一点 我问这个是因为我使用了一些c#代码并且我不断将私有方法更改为public用于测试目的,因为许多中间私有方法(如用于xml序列化的字符串生成器)对于调试目的非常有用(比如写一些字符串的部分)记录文件等。) 所以我的问题是:
是否存在通过访问限制实现的任何内容,但在不限制访问的情况下通过命名约定无法实现?

6 个答案:

答案 0 :(得分:9)

您提出了几个问题/问题,因此我将分别处理每个问题/问题。

如何测试私有方法?

debate/discussion of if you should test private methods之外,还有几种方法可以做到这一点。

<强>重构
一般的答案是,您可以将行为重构为原始类所利用的单独的可测试类。这是有争议的,并不总是适用,具体取决于您的设计或特权。

<强> InternalsVisibleTo
常见的例程是将可测试逻辑提取到方法中并将其标记为internal。在装配属性中,您可以添加属性[InternalsVisibleTo("MyUnitTestingProject")]。这将允许您从单元测试项目访问该方法,同时仍然隐藏对所有其他程序集的访问权限。 http://msdn.microsoft.com/en-us/library/system.runtime.compilerservices.internalsvisibletoattribute.aspx

但是,鉴于您提出的意见,您无法在工作场所永久更改源代码的结构;您正在更改访问者以进行测试,测试,然后在提交之前将其更改回来。在这种情况下,有两种选择:

部分测试类
如果您将班级标记为partial。创建第二个部分类文件,其中包含您的测试(或私有成员的公共包装)。然后,当需要合并/提交时,只需从项目中删除部分类,并从主类中删除partial关键字。此外,您可以使用if DEBUG(或其他指令)包装整个测试代码文件,因此它仅在单元测试时可用,并且不会影响生产/开发代码。 http://weblogs.asp.net/ralfw/archive/2006/04/14/442836.aspx

public partial class MyClass
{
    private string CreateTempString()
    {
        return "Hello World!";
    }
}

#if DEBUG

public partial class MyClass //in file "MyClass_Accessor.cs"
{
    public string CreateTempString_Accessor()
    {
        return CreateTempString();
    }
}

#endif

<强>反射
您仍然可以通过反射访问私人会员:     公共课测试     {         私人字符串PrivateField =&#34; private&#34 ;;     }

Test t = new Test();
var publicFieldInfo = typeof(Test).GetField("PrivateField", BindingFlags.Instance | BindingFlags.NonPublic);
Console.WriteLine(publicFieldInfo.GetValue(t)); //outputs "private"

您的单元测试可以通过这种方式提取类中的私有/隐藏数据。事实上,Microsoft提供了两个完全适合您的类:PrivateObjectPrivateType

鉴于您的内部开发流程限制,这可能是您最好的选择,因为您可以在主项目库之外管理自己的测试,而无需更改核心代码中的任何内容。

注意 Silverlight(以及可能的其他Core-CLR运行时)在反射过程中严格执行非公共访问,因此此选项在这些情况下不适用。

所以,有几种方法可以测试私人成员,而且我确信有一些更聪明/不那么聪明的方法可以潜伏在那里。


通过使用__前缀命名所有私有方法或引入私有但可访问的访问修饰符,可以实现所有这些好处吗?

您引用的好处(引用他人)是:

  1. 使实现更清洁/避免长垂直列表 IDE自动完成中的方法
  2. 向全世界宣布哪些方法是公共接口和 这可能会改变,仅仅是为了实现目的
  3. 可读性
  4. 现在你补充说,通过改变语言规范和/或支持私有但可访问的访问修饰符的IDE,可以通过__ 实现这些,可能有些不安全 - 像关键字,会阻止这一点。我不认为讨论更改语言和IDE的当前功能/行为(并且可能对StackOverflow没有意义)是值得的,所以关注 可用的内容:

    1)清洁实施和智能感知
    Visual Studio IDE(我不能代表MonoDevelop)确实支持在用[EditorBrowsableAttribute]标记成员时隐藏智能感知。但这只适用于开发人员启用选项&#34;隐藏高级会员&#34;在他们的Visual Studio选项中。 (请注意,当您在同一个程序集中工作时,它不会抑制智能感知中的成员) http://msdn.microsoft.com/en-us/library/system.componentmodel.editorbrowsableattribute.aspx

    因此,标记一个公共成员使其表现为(intellisense-wise)为internal-ish(没有[InternalsVisibleTo]支持)。因此,如果您在同一个程序集中,或者如果您没有启用Hide Advanced Members,那么您仍会在intellisense中看到一长串__成员。即使你将它隐藏在智能感知中,它仍然可以根据其当前的访问修饰符完全访问。

    2)公共使用界面/合同
    这假设C#,Visual Basic,F#,C ++ .NET和任何.NET开发世界中的所有开发人员都将采用相同的__命名约定,因为程序集已编译和互换开发者之间。也许如果您在IronPython中编写脚本,您可以逃脱它,或者如果您的公司内部采用这种方法。但总的来说,它不会发生,.NET开发人员可能会因为采用这种惯例来利用库,因为这不是一般的.NET文化。

    3)可读性
    这种情况与#2相符,因为它是可读的&#34;取决于文化以及该领域的开发人员期望的内容;这肯定是有争议和主观的。我敢打赌,大多数C#开发人员都会发现严格/强制的封装,以显着提高代码的可读性,我相信他们中的很大一部分会发现__常常会减损它。 (作为一方,我确信开发人员为私有字段采用_或__前缀并且仍将其保密为私有并不罕见)

    然而,C#中的可读性和封装不仅仅是公共/私有访问器。在C#中,有私有,公共,受保护的内部,受保护和内部(我是否缺少一个?),每个都有自己的用途,并为开发人员提供不同的信息。现在我不确定你将如何通过__进行这些访问器的通信。建议单下划线是受保护的,双下划线是私有的, 肯定会妨碍可读性。


    是否存在通过命名约定无法限制访问而无法实现访问限制的任何内容?

    如果你问为什么 C#设计团队走了这条路,那么我想你有一天不得不问Hejlsberg先生。我知道他们正在创建一种语言,收集C / C ++的最佳部分,并强烈关注面向对象原则的原则。

    关于通过访问修饰符强制执行访问所实现的目标: 更确保消费者对API的正确访问。如果您的类使用MakeTempStringForXMLSerialization方法将字符串存储为序列化的类属性,但出于性能原因需要进行代价高昂的检查(因为您作为开发人员已经进行了单元测试以确保所有类的#c; s字段将通过公共API有效)然后第三方做一些可爱的垃圾垃圾,他们会责怪你和/或供应商为一个劣质库。那是公平的吗?不必要;他们把垃圾放进去,但现实中很多人仍会责怪供应商。

    对于尝试了解API工作原理的新开发人员,有助于简化他们的体验。是的,开发人员应该阅读文档,但是如果公共API是直观的(通常应该是这样)并且没有公开那些不应该访问的成员,那么它就是&#39;更不容易混淆,他们不太可能意外地将垃圾送入系统。它还可以降低开销,让开发人员有效地使用和利用您的API而不会有麻烦。当您发布API的任何更新时,尤其如此,您希望更改/重构/改进内部实现细节。

    因此,从业务角度来看,它可以保护他们免受责任和与客户的不良关系,并且更有吸引力的是开发商购买和投资。

    现在情况就是这样,正如你所说的,如果每个人都遵循惯例,__成员不应该在课堂外访问,或者在你说的地方提供一些unsafe标记,&# 34;如果你这样做,它就会破裂,这不是我的错!&#34;那么你就不会和C#.NET开发在同一个星球上。 C#提供的访问器提供__约定,但确保所有开发人员都遵守它。

    有人可能会争辩说限制访问是一种错觉,因为消费者可以通过反射解决它(如上所示),因此在访问修饰符和__之间实际上没有编程差异(或其他)表示法。 (在Silverlight / Core-CLR上,尽管如此,绝对是的程序化差异!)但是开发人员可以访问这些私有字段的工作是给消费者一个开放的门与签署&#34;不要进入&#34; (你希望他们可以阅读)和一把带锁的门,他们必须打击。

    那么到底它实际提供了什么? 对成员的标准化,强制访问权限,其中__为成员提供非标准化,非强制访问权限。此外,__缺乏各种可用访问修饰符提供的描述范围。

    更新(2013年1月2日)

    我知道它已经过了半年,但我一直在阅读C#语言规范,并从2.4.2 Identifiers部分中看到了这个小宝石,其中指出:

      

    包含两个连续下划线字符的标识符(U + 005F)   保留供实施使用。例如,一个   实现可能会提供以两个开头的扩展关键字   下划线。

    我认为没有必要发生 bad ,如果你这样做,很可能没有什么会破坏灾难。但是,当您考虑在代码中使用双下划线时,应该考虑的另一个方面是规范建议您不这样做。

答案 1 :(得分:4)

存在私有方法的原因是提供encapsulation

这允许您提供您希望对象进行交互的公共合同,同时以重构不会影响消费者的方式封装您的内部逻辑和状态。

例如,您可以提供公共命名属性,但决定将状态存储在Dictionary中,类似于DataSet所做的类型。

它不是真正的“安全”功能(因为你总是有反射来覆盖它),但是一种将公共API与内部实现分开的方法。 没有人应该“依赖”您的内部和私人实施,它们应该仅依赖于您的公共(或受保护)实施。

现在,关于单元测试,通常不希望测试内部实现。 一种常见的方法是将其声明为内部,并通过InternalsVisibleToAttribute提供对测试程序集的访问,如Chris所述。

此外,public,private和protected之间的明确区别对于继承,定义您希望覆盖的内容以及不应该覆盖的内容非常有用。

答案 2 :(得分:1)

通常,将字段或方法标记为私有没有任何意义。它提供了一种人为的安全感。所有代码都在同一个进程中运行,大概是由彼此友好关系的人编写的。为什么他们需要访问控制来保护代码?这听起来像是一个文化问题。

文档提供了比私有/受保护/公共更多的信息,一个好的系统无论如何都会有文档。用它来指导您的开发。如果你将一个方法标记为“私有”,并且开发人员无论如何都要调用它,那么这是一个糟糕的开发人员,他最终会以其他方式破坏你的系统。修复该开发人员。

访问控制最终会阻碍开发,并且您只需花时间让编译器满意。

当然,如果您严格谈论C#,那么您应该坚持将标记方法标记为私有,因为其他C#开发人员会期望它。

答案 3 :(得分:1)

它隐藏了许多内部细节,特别是对于实际上可能想要隐藏这些细节的图书馆实施者。

请记住,那里有商业图书馆,正在出售。他们在用户界面中只公开了一组非常有限的选项,他们希望其他所有选项都能被隐藏起来。

如果您设计的语言不提供此选项,那么您将制作一种几乎专门用于开源项目(或主要用于脚本编写)的语言。 我对Python知之甚少,知道是否有用python编写的商业闭源库会很有趣。

你提到的理由也很好。

答案 4 :(得分:1)

如果您要发布包含公共成员的库,则用户可以使用这些成员。您可以使用命名约定来告诉他们不应该这样做,但有些人会这样做。

如果您的“内部”成员是公开的,则您无法保证与旧版本库的兼容性,除非您保持所有内部结构相同并且仅进行非常小的更改。这意味着使用您的库的每个程序都必须使用它编译的版本(并且最有可能通过发布自己的版本,而不是使用共享系统版本)。

当您的库具有安全更新时,这将成为一个问题。最好的情况是,所有程序都使用不同的共享版本,您必须将安全更新向后移植到每个版本中。更有可能的是,每个程序都有自己的版本,除非该程序的作者这样做,否则根本不会得到更新。

如果您仔细设计公共API,并使用私人成员来处理您可能更改的内容,则可以将更改保持在最低限度。然后,所有程序/库都可以使用最新版本的库,即使它们是为早期版本编译的,也可以从更新中受益。

答案 5 :(得分:0)

这可能会引发一些有趣的争论。标记私有方法(和成员变量)的主要原因是强制隐藏信息。外部代码不应该知道或关心你的课程是如何工作的。你可以自由地改变你的impl而不会破坏其他东西。这是完善的软件实践。

然而,在现实生活中,私有方法可能妨碍测试和无法预见的变化。 Python有一种信念“我们都同意成年人”并且不鼓励使用私人方法。因此,有时我会使用命名约定和/或评论“不打算供公众使用”。这是OO理论与现实问题中的另一个。