为什么在内部类中使用公共方法
本文关键字:方法 内部类 为什么 | 更新日期: 2023-09-27 18:24:40
我们的一个项目中有很多代码如下所示:
internal static class Extensions
{
public static string AddFoo(this string s)
{
if (s == null)
{
return "Foo";
}
return $({s}Foo);
}
}
除了"以后更容易公开类型"之外,还有什么明确的理由这样做吗?
我怀疑它只在非常奇怪的边缘情况下才重要(Silverlight中的反射),或者根本不重要。
更新:这个问题是我2014年9月博客的主题。谢谢你的提问!
即使在编译器团队内部,这个问题也存在相当大的争论。
首先,了解规则是明智的。类或结构的公共成员是任何可以访问包含类型的对象都可以访问的成员。因此,内部类的公共成员实际上是内部的。
那么,现在,给定一个内部类,您希望在程序集中访问的它的成员应该标记为public还是internal?
我的意见是:将这些成员标记为公众。
我用"public"的意思是"这个成员不是一个实现细节"。受保护的成员是一个实现细节;它需要一些东西来使派生类工作。内部成员是一个实现细节;此程序集内部的其他内容需要该成员才能正常工作。一个公共成员说"这个成员代表了这个对象提供的关键的、文档化的功能。"
基本上,我的态度是:假设我决定把这个内部类变成一个公共类。为了做到这一点,我只想更改一件事:类的可访问性。如果将内部类转换为公共类意味着我还必须将内部成员转换为公共成员,那么该成员是该类公共表面积的一部分,并且它首先应该是公共的。
其他人不同意。有一个特遣队说,他们希望能够浏览成员的声明,并立即知道是否只从内部代码调用它。
不幸的是,这并不总是很好;例如,实现内部接口的内部类仍然必须将实现成员标记为public,因为它们是类公共表面的一部分。
如果类是internal
,那么从可访问性的角度来看,标记方法internal
还是public
并不重要。然而,如果类是public
,那么使用您将使用的类型仍然是好的。
虽然有些人说这简化了从internal
到public
的转换。它也是该方法描述的一部分。Internal
方法通常被认为对于不受限制的访问是不安全的,而public
方法被认为是(大部分)免费游戏。
通过像在public
类中那样使用internal
或public
,可以确保您正在传达预期的访问风格,同时也可以简化将来制作public
类所需的工作。
我怀疑"以后更容易公开类型?"
作用域规则意味着该方法将只显示为internal
,因此这些方法标记为public
还是internal
并不重要。
想到的一种可能性是,类是公共的,后来被更改为internal
,开发人员没有费心更改所有的方法可访问性修饰符。
我经常在内部类中标记我的方法为public,而不是internal,因为a)这并不重要,b)我故意使用internal来指示该方法是内部的(我不想在公共类中公开这个方法是有原因的。因此,如果我有一个内部方法,在将其更改为公共方法之前,我必须了解它是内部方法的原因,而如果我在内部类中处理公共方法,我真的必须思考为什么该类是内部的,而不是为什么每个方法都是内部的。
在某些情况下,也可能是内部类型实现了一个公共接口,这意味着在该接口上定义的任何方法仍然需要声明为公共。
同样,公共方法也会被标记为内部方法,因为它在内部类中,但它有一个advantaje(正如你猜测的那样),如果你想将类标记为公共,你必须更改更少的代码。
原因与在任何其他类中使用公共方法相同,因此它们对包含类型的外部是公共的。
类型的访问修饰符与其成员的访问修饰符完全没有关系。这两个决定是完全独立作出的。
仅仅因为类型和成员修饰符的某些组合产生了看似相同的结果(或者其他人称之为"有效"),并不意味着它们在语义上是相同的。
实体的本地访问修饰符(如代码中所声明的)和其全局有效访问级别(如通过包含链评估的)也是完全不同的。在一栋锁着的大楼里,一间开放的办公室仍然开放,即使你不能真正从街上进入。
不要考虑最终效果。首先考虑一下你在当地需要什么。
Public's Public
:经典情景Public's Internal
:类型是公共的,但您希望在程序集中有一些半合法的访问权限来做一些古怪的事情Internal's Public
:您隐藏了整个类型,但在程序集中它有一个经典的公共表面Internal's Internal
:我想不出任何现实世界的例子。也许有什么东西很快就会成为公众的内部
CCD_ 21与CCD_ 22是一个错误的困境。这两者的含义完全不同,应该在各自的情况下使用,不重叠。
internal
表示只能从同一程序集中访问成员。该程序集中的其他类可以访问internal public
成员,但不能访问private
或protected
成员,无论是否访问internal
。
我今天真的很挣扎。到目前为止,我会说,如果类是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所说。
到目前为止,我从未真正停下来思考过,我只是接受了它,但在阅读了埃里克的帖子后,它真的让我思考了很多,经过深思熟虑,它很有意义。
确实存在差异。在我们的项目中,我们制作了很多内部类,但我们在另一个程序集中进行单元测试,在程序集信息中,我们使用InternalsVisibleTo允许UnitTest程序集调用内部类。我注意到,如果内部类有内部构造函数,出于某种原因,我们无法在单元测试程序集中使用Activator.CreateInstance创建实例。但是,如果我们将构造函数更改为public,但类仍然是内部的,那么它可以正常工作。但我想这是一个非常罕见的情况(就像埃里克在最初的帖子中所说的:反思)。
我想我对此还有其他看法。一开始,我想知道在内部类中向公共声明一些东西有什么意义。然后我在这里结束了,读到如果你后来决定把课程改为公开课,那可能会很好。没错。因此,在我的脑海中形成了一个模式:如果它不改变当前行为,那么就宽容一些,允许在当前代码状态下没有意义(也不会造成伤害)的事情,但后来如果你改变类的声明,它会的
像这样:
public sealed class MyCurrentlySealedClass
{
protected void MyCurretlyPrivateMethod()
{
}
}
根据我上面提到的"模式",这应该很好。它遵循同样的想法。它的行为类似于private
方法,因为您不能继承该类。但如果删除sealed
约束,它仍然有效:继承的类可以看到这个方法,这绝对是我想要实现的。但你会得到一个警告:CS0628
或CA1047
。它们都是关于不要在sealed
类中声明protected
成员。此外,我发现完全一致,认为这是毫无意义的:"密封类中受保护的成员"警告(一个单例类)
因此,在这一警告和讨论联系起来之后,我决定在内部课堂上,把所有的东西都放在内部或更少的地方,因为它更符合这种思维,我们不会混合不同的"模式"。
这与您的示例无关,但对于在C#中实现接口的类(包括7.3),它是:
internal interface IAmOnlyInternalViewable
{
void CallMeOnlyInsideAssembly();
//internal void CallMeOnlyInsideAssembly(); // <-- is not possible up to C# 7.3 inclusive
}
internal class OnlyInternalVisible : IAmOnlyInternalViewable
{
public void CallMeOnlyInsideAssembly() { } //Must be public to implement the interface
}
因此,如果您想要一个实现interface
的internal class
,那么实现的方法需要是public
。