为啥在内部类中使用公共方法?

Posted

技术标签:

【中文标题】为啥在内部类中使用公共方法?【英文标题】:Why use a public method in an internal class?为什么在内部类中使用公共方法? 【发布时间】:2012-03-07 07:36:51 【问题描述】:

我们的一个项目中有很多代码如下所示:

internal static class Extensions

    public static string AddFoo(this string s)
    
        if (s == null)
        
            return "Foo";
        

        return $(sFoo);
    

除了“以后将类型公开更容易”之外,是否有任何明确的理由这样做?

我怀疑它只在非常奇怪的边缘情况(Silverlight 中的反射)中很重要,或者根本不重要。

【问题讨论】:

根据我的经验,这是正常的做法。 @phoog,好的。但为什么这是正常做法?也许比将一堆方法更改为内部方法更容易?快速修复 --> 将类型标记为内部? 这是我一直认为的。但是,我没有经过深思熟虑的分析,这就是为什么我没有发布答案。 Eric Lippert 的回答很好地总结了我的想法,同时也提出了一个潜伏在我脑海中试图寻找出路的东西:接口成员的隐式实现必须是公共的。 那个方法不等于return s + "Foo";吗? + 运算符不关心 null 或空字符串。 【参考方案1】:

更新:这个问题是the subject of my blog in September 2014。谢谢你的好问题!

即使在编译器团队内部也存在相当多的争论。

首先,了解规则是明智的。类或结构的公共成员是可以访问包含类型的任何对象的成员。所以内部类的公共成员实际上是内部的。

那么现在,给定一个内部类,您希望在程序集中访问的它的成员应该标记为公共还是内部?

我的意见是:将此类成员标记为公开。

我使用“public”来表示“此成员不是实现细节”。受保护的成员是一个实现细节;要使派生类工作,需要一些关于它的东西。内部成员是实现细节;该程序集内部的其他东西需要该成员才能正常工作。一个公共成员说“这个成员代表了这个对象提供的关键的、记录在案的功能。”

基本上,我的态度是:假设我决定把这个内部类变成一个公共类。为了做到这一点,我想只改变一件事:类的可访问性。如果将内部类转换为公共类意味着我还必须将内部成员转换为公共成员,那么该成员是该类的公共表面区域的一部分,它应该是首先公开。

其他人不同意。有一个特遣队说他们希望能够看一眼成员的声明并立即知道它是否只会从内部代码中调用。

不幸的是,这并不总是很好。例如,实现内部接口的内部类仍然必须将实现成员标记为公共,因为它们是类的公共表面的一部分

【讨论】:

我喜欢这个答案...它非常符合我对编写尽可能自我记录的代码的态度,并且范围是传播意图的好方法,特别是如果您的其他团队成员了解您的约定。 +1 表达了我想说的,但比我能表达的要好得多(也用于提出界面角度,尽管我注意到它仅适用于隐式成员实现)。跨度> 接口部分很重要——不可能使用内部实现接口方法,它要么是公共的,要么是显式的接口声明。所以public这个词有两种含义。 从理想的角度来看,您支持public 的论点非常有说服力。然而,我发现“并不总是很好地工作......”特别强大。失去一致性会贬低任何“一目了然”的好处。以这种方式使用internal 意味着故意建立有时是错误的直觉。拥有一个最正确的直觉是 IMO 对编程来说是一件可怕的事情。 您博客文章中的重要引述:“我的建议是在您的团队中讨论这个问题,做出决定,然后坚持下去。”【参考方案2】:

如果类是internal,则从可访问性的角度来看,标记方法internal 还是public 都没有关系。但是,如果类是 public,则使用您将使用的类型仍然很好。

虽然有人说这可以简化从internalpublic 的转换。它也用作方法描述的一部分。 Internal 方法通常被认为对于不受限制的访问是不安全的,而 public 方法被认为是(大部分)免费游戏。

通过使用 internalpublic 就像在 public 类中一样,您可以确保您传达预期的访问方式,同时还简化了在未来。

【讨论】:

我倾向于最大限度地限制一切,同时创建一个好的 API 并允许我的目标消费者做我想让他们做的事情。如果课程是公开的,我也会和你一起标记成员。更具体地说,如果我认为它总是安全的,我只会将任何成员标记为 public。否则其他人稍后将类标记为公共(它发生)可能会暴露非安全成员。 @Eric:如果您想在准备好之前放弃确定方法的安全性,那很好,但我仅指的是被认为安全的方法,在这种情况下不会暴露。跨度> 【参考方案3】:

我经常在内部类中将我的方法标记为 public 而不是 internal 作为 a) 这并不重要 b) 我使用 internal 来表示该方法是内部故意的(有一些原因我不想要在公共类中公开此方法。因此,如果我有一个内部方法,我真的必须在将其更改为公共之前了解它是内部的原因,而如果我正在处理内部类中的公共方法,我真的必须考虑关于为什么类是内部的,而不是为什么每个方法都是内部的。

【讨论】:

感谢您的回答。不过,我倾向于固执地坚持“一切”在编码时都很重要:) 你说得对,埃里克,这有点被抛弃了。希望我的其余回答有一些用处。我认为 Eric Lippert 的回答正是我想要表达的,但他解释得更好。【参考方案4】:

我怀疑“以后公开类型更容易吗?”是吗。

范围规则意味着该方法将仅以internal 可见 - 因此方法标记为public 还是internal 并不重要。

想到的一种可能性是该类公开的,后来更改为internal,开发人员没有费心更改所有方法的可访问性修饰符。

【讨论】:

+1 用于解决明显的“公共类变为内部”场景。【参考方案5】:

在某些情况下,也可能是内部类型实现了公共接口,这意味着在该接口上定义的任何方法仍需要声明为公共。

【讨论】:

【参考方案6】:

同样,public 方法将被真正标记为 internal,因为它在一个内部类中,但它有一个优点(如您所说),如果您想将该类标记为 public,您必须更改更少的代码.

【讨论】:

已有相关问题:***.com/questions/711361/…【参考方案7】:

出于与在任何其他类中使用公共方法相同的原因 - 以便它们对包含类型的外部是公共的。

类型的访问修饰符与其成员的访问修饰符完全为零。这两个决定是完全独立做出的。

仅仅因为类型和成员修饰符的某些组合产生看似(或其他人称之为“有效”)相同的结果并不意味着它们在语义上是相同的。

实体的本地访问修饰符(在代码中声明)和它的全局有效访问级别(通过包含链评估)也是完全不同的东西。上锁的建筑物内的开放式办公室仍然开放,即使您无法真正从街上进入。

不要考虑最终效果。首先考虑您在本地需要什么。

Public's Public:经典情景。 Public's Internal:类型是公开的,但您希望在程序集中获得一些半合法的访问权限来做一些 hacky-wacky 的事情。 Internal's Public: 你隐藏了整个类型,但在程序集中它有一个经典的公共表面 Internal's Internal:我想不出任何现实世界的例子。也许很快就会在内部公开?

Internal's PublicInternal's Internal 是一个错误的两难选择。两者有完全不同的含义,应该在各自的情况下使用,不重叠。

【讨论】:

另一种思考方式。我从接受的答案中得到了我需要的东西,但这也很有用。 “先想想你在当地需要什么。”罗杰。【参考方案8】:

internal 表示只能从同一个程序集中访问该成员。该程序集中的其他类可以访问internal public 成员,但不能访问privateprotected 成员,internal 或不。

【讨论】:

但是如果方法被标记为internal 而不是public,它们的可见性不会改变。我相信这就是 OP 所要求的。 @zapthedingbat - 这个帖子实际上是正确的,但它真的回答了这个问题吗? 对,问题是为什么要这样标记它们。我了解范围的基础知识。不过,感谢您的尝试!【参考方案9】:

我今天确实为此苦苦挣扎。到目前为止,我会说,如果类是 internal,则方法都应该用 internal 标记,并且会认为其他任何事情都只是糟糕的编码或懒惰,特别是在企业开发中;但是,我必须继承 public 类并覆盖其中一个方法:

internal class SslStreamEx : System.Net.Security.SslStream

    public override void Close()
    
        try
        
            // Send close_notify manually
        
        finally
        
            base.Close();
        
    

方法必须是 public,这让我想到将方法设置为 internal 确实没有逻辑意义,除非它们确实必须是,正如 Eric Lippert 所说。

直到现在我从来没有真正停下来思考过,我只是接受了它,但是在阅读了 Eric 的帖子后,它真的让我思考,经过深思熟虑,这很有意义。

【讨论】:

【参考方案10】:

确实有区别。 在我们的项目中,我们制作了很多内部类,但我们在另一个程序集中进行单元测试,并且在我们的程序集信息中,我们使用 InternalsVisibleTo 来允许 UnitTest 程序集调用内部类。 我注意到如果内部类有一个内部构造函数,由于某种原因,我们无法在单元测试程序集中使用 Activator.CreateInstance 创建实例。但是如果我们将构造函数更改为 public 但类仍然是内部的,它就可以正常工作。 但我想这是一个非常罕见的情况(就像 Eric 在原帖中所说:Reflection)。

【讨论】:

【参考方案11】:

我想我对此有额外的看法。起初,我想知道在内部类中向 public 声明一些东西有什么意义。然后我在这里结束了,读到如果您以后决定将课程更改为公开课程可能会很好。真的。于是,我的脑海中形成了一个模式如果它不改变当前的行为,那就放任自流,允许在当前状态下没有意义(并且不会造成伤害)的事情发生代码,但稍后如果您更改类的声明,它会。

像这样:

public sealed class MyCurrentlySealedClass

    protected void MyCurretlyPrivateMethod()
    
    

根据我上面提到的“模式”,这应该是完全可以的。它遵循相同的想法。它表现为private 方法,因为您不能继承该类。但是如果你删除sealed约束,它仍然有效:继承的类可以看到这个方法,这绝对是我想要实现的。但是您会收到警告:CS0628CA1047。他们俩都是关于不要在sealed 类中声明protected 成员。此外,我完全同意,这是毫无意义的:'Protected member in sealed class' warning (a singleton class)

因此,在此警告和讨论链接之后,我决定将所有内容都设置为内部或更少,在内部类中,因为它更符合这种想法,并且我们不会混合不同的“模式”。

【讨论】:

我非常喜欢按照 Eric Lippert 所说的方式(或至少出于原因)这样做。他的文章绝对值得一读。

以上是关于为啥在内部类中使用公共方法?的主要内容,如果未能解决你的问题,请参考以下文章

为啥我不能在另一个类中调用公共方法?

为啥我们应该将接口方法声明为公共的? [复制]

在内部类中执行main方法[重复]

Javascript进阶---编写类

为啥 Vec::len 是方法而不是公共属性?

为啥非 const 方法是私有的时不调用公共 const 方法?