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


250

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

internal static class Extensions
{
    public static string AddFoo(this string s)
    {
        if (s == null)
        {
            return "Foo";
        }

        return $({s}Foo);
    }
}

除了“以后更容易公开该类型?”以外,还有其他明确的理由吗?

我怀疑这仅在非常奇怪的情况下(在Silverlight中反射)才重要,或者根本不重要。


1
以我的经验,这是正常的做法。
phoog 2012年

2
@phoog,好的。但是为什么这是正常做法?然后将一堆方法更改为内部方法可能更容易些?快速修复->标记内部类型?
bopapa_1979

这就是我一直认为的。但是,我没有经过深思熟虑的分析,这就是为什么我没有发布答案的原因。
phoog 2012年

2
埃里克·利珀特(Eric Lippert)的回答很好地概括了我的想法,并且还提出了我的脑海中潜藏的一些东西,试图寻找出路:接口成员的隐式实现必须是公共的。
phoog 2012年

这种方法不等于return s + "Foo";吗?该+运营商不关心空或空字符串。
米克尔·隆德

Answers:


418

更新:这个问题是2014年9月我博客的主题。感谢您提出的好问题!

即使在编译器团队内部,也对此问题进行了大量辩论。

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

因此,现在给定一个内部类,您要在程序集中访问的成员应该标记为公共成员还是内部成员?

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

我用“ public”来表示“此成员不是实现细节”。受保护的成员是实现的详细信息;使派生类工作需要一些有关它的内容。内部成员是实现细节。该装配体内部的其他零件需要该成员才能正常工作。公开成员说:“该成员代表此对象提供的关键的,已记录的功能。”

基本上,我的态度是:假设我决定将此内部课程变成公共课程。为此,我只想更改一件事:类的可访问性。如果将内部班级转变为公共班级意味着我也必须将内部成员转变为公共班级,那么该成员是该班级公共区域的一部分,那么它首先应该是公共的。

其他人则不同意。有一个队伍称他们希望能够看一眼成员的声明,并立即知道是否仅从内部代码中调用它。

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


11
我喜欢这个答案……这与我对待编写尽可能自我记录的代码的态度非常吻合,并且范围是一种传达意图的好方法,尤其是在您的其他团队成员理解您的惯例的情况下。
bopapa_1979

10
+1是说我想说的内容,但其表达方式要比我的能力好得多(还提出了接口角度,尽管我注意到这仅适用于隐式成员实现)。
phoog 2012年

2
接口部分很重要-无法使用内部方法来实现接口方法,它可以是公共的或显式的接口声明。因此,“公共”一词有两个含义。
Michael Stum

8
从概念上讲,您赞成的论点public非常有说服力。但是,我发现“并非总是能很好地完成工作……”功能特别强大。失去一致性会使任何“一览无余”的利益贬值。internal以这种方式使用意味着刻意建立有时是错误的直觉。IMO的直觉非常正确,这对于编程来说是一件可怕的事情。
布赖恩

3
博客文章中的重要一句话是:“我的建议是与团队讨论问题,做出决定,然后坚持下去。”
bopapa_1979

17

如果类是类internal,那么从可访问性的角度来看,标记方法internal还是都没关系public。但是,最好使用类为时使用的类型public

尽管有人说过,这样可以简化从internal到的过渡public。它也作为方法描述的一部分。Internal方法通常被认为对不受限制的访问是不安全的,而方法通常被认为public是免费游戏。

通过使用internalpublic像在public课堂上那样使用,可以确保传达期望的访问风格,同时还减轻public了将来上课的工作量。


在创建一个良好的API并允许我的目标用户执行我打算他们做的事情的同时,我倾向于最大程度地限制一切。我也与您一起标记成员,就像在公开课上一样。更具体地说,如果我认为它总是安全的,那么我只会将任何成员标记为公众。否则,稍后再将班级标记为公开的其他人(发生)可能会暴露不安全的成员。
bopapa_1979

@Eric:如果您想在准备就绪之前就放弃确定方法的安全性,那很好,但是我仅指的是被认为是安全的方法,在这种情况下,没有任何暴露。
古凡特

10

我经常在内部类中将我的方法标记为公共而不是内部,因为a)没关系,b)我使用internal表示该方法是有目的的内部存在(出于某些原因,我不想公开此方法因此,如果我有一个内部方法,那么在将其更改为公共方法之前,我真的必须先了解它内部的原因,而如果我要在一个内部类中处理一个公共方法,我真的必须考虑为什么类是内部的,而不是为什么每个方法都是内部的。


感谢你的回答。我倾向于固执地坚持,“一切”都在编码时很重要,但:)
bopapa_1979 2012年

1
您是正确的埃里克(Eric),这有点丢掉评论。希望我的其余回答有用。我想埃里克·利珀特(Eric Lippert)的答案就是我想要表达的,但他的解释要好得多。
ƉiamondǤeezeƦ

8

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

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

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


1
+1表示明显的“公共阶层成为内部”场景。
bopapa_1979



0

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


但是,如果标记方法internal而不是方法public,则它们的可见性不会改变。我相信这就是OP的要求。
Oded 2012年

1
@zapthedingbat-该帖子实际上是正确的,但实际上是在回答问题吗?
Oded 2012年

1
是的,问题是为什么要用这种方式标记它们。我了解范围的基础知识。不过,感谢您的尝试!
bopapa_1979

0

我今天实际上为此感到挣扎。到现在为止,我会说,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的帖子之后,它确实让我开始思考,并且经过大量的思考,这很有道理。


0

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


0

我想对此还有其他意见。最初,我想知道在内部类中向公共声明某些内容的合理性。然后我到这里结束,读到如果您以后决定将班级改为公开授课,那可能很好。真正。因此,在我的脑海中形成了一种模式如果它不会改变当前的行为,那就放任自流,并允许在当前代码状态下没有任何意义(并且不会造成伤害)的事物,但是后来,如果您更改类的声明。

像这样:

public sealed class MyCurrentlySealedClass
{
    protected void MyCurretlyPrivateMethod()
    {
    }
}

根据我上面提到的“模式”,这应该很好。它遵循相同的想法。private由于您不能继承该类,因此它的行为就像一个方法。但是,如果删除sealed约束,它仍然有效:继承的类可以看到此方法,这绝对是我想要实现的。但是您会收到警告:CS0628CA1047。两者都是关于不要protectedsealed类中声明成员的。此外,我已经完全同意,这是毫无意义的:“受保护的成员处于密封等级”警告(单身人士等级)

因此,在发出警告并进行讨论之后,我决定在一个内部类中将所有内容内部化或更少化,因为它更符合这种思维方式,并且我们不混合使用不同的“模式”。


我非常喜欢这样做(至少出于某种原因),埃里克·利珀特(Eric Lippert)说。他的文章绝对值得一读。
bopapa_1979 '18
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.