462

你能解释一下internalC#中关键字的实际用法吗?

我知道internal修饰符限制了对当前程序集的访问,但是我应该在什么时候以及在什么情况下使用它?

4

22 回答 22

405

您希望从同一程序集中的许多其他类访问的实用程序或帮助程序类/方法,但您希望确保其他程序集中的代码无法访问。

MSDN(通过 archive.org):

内部访问的一个常见用途是在基于组件的开发中,因为它使一组组件能够以私有方式协作,而不会暴露给应用程序的其余代码。例如,用于构建图形用户界面的框架可以提供 Control 和 Form 类,它们使用具有内部访问权限的成员进行协作。由于这些成员是内部成员,因此它们不会暴露于使用框架的代码。

您还可以使用 internal 修饰符和InternalsVisibleTo程序集级别属性来创建“朋友”程序集,这些程序集被授予对目标程序集内部类的特殊访问权限。

这对于创建单元测试程序集很有用,然后允许调用要测试的程序集的内部成员。当然,没有其他程序集被授予此级别的访问权限,因此当您发布系统时,将保持封装。

于 2008-10-03T04:51:48.363 回答
101

如果 Bob 需要 BigImportantClass,那么 Bob 需要让拥有项目 A 的人注册,以保证 BigImportantClass 将被编写以满足他的需求,经过测试以确保它满足他的需求,被记录为满足他的需求,并且一个过程将到位,以确保它永远不会改变,以不再满足他的需要。

如果一个类是内部的,那么它就不必经过那个过程,这节省了项目 A 的预算,他们可以花在其他事情上。

内部的意义不是它让鲍勃生活困难。正是因为它允许您控制项目 A 对特性、生命周期、兼容性等做出的昂贵承诺。

于 2010-06-10T16:05:49.367 回答
67

使用 internal 的另一个原因是如果你混淆了你的二进制文件。混淆器知道打乱任何内部类的类名是安全的,而公共类的名称不能打乱,因为这可能会破坏现有的引用。

于 2008-10-04T03:08:37.730 回答
37

如果您正在编写一个将大量复杂功能封装到一个简单的公共 API 中的 DLL,那么“内部”用于不公开公开的类成员。

隐藏复杂性(又名封装)是质量软件工程的主要概念。

于 2008-10-03T13:19:27.857 回答
20

当您在非托管代码上构建包装器时,会大量使用 internal 关键字。

当您有一个要 DllImport 的基于 C/C++ 的库时,您可以将这些函数作为类的静态函数导入,并使其成为内部函数,因此您的用户只能访问您的包装器而不是原始 API,因此它不能乱七八糟。对于您需要的多个包装类,您可以在程序集中的任何地方使用静态函数。

您可以查看 Mono.Cairo,它是使用这种方法的 cairo 库的包装器。

于 2008-10-04T03:31:37.870 回答
16

在“尽可能使用严​​格的修饰符”规则的驱动下,我在需要访问另一个类的方法(例如,在我明确需要从另一个程序集访问它之前)的任何地方都使用内部。

由于汇编接口通常比其类接口的总和更窄,所以我使用它的地方很多。

于 2008-10-03T10:51:38.007 回答
12

我发现内部被过度使用了。你真的不应该只将某些功能暴露给某些你不会暴露给其他消费者的类。

在我看来,这破坏了接口,破坏了抽象。这并不是说永远不应该使用它,但更好的解决方案是重构为不同的类或尽可能以不同的方式使用。然而,这可能并不总是可能的。

它可能导致问题的原因是另一个开发人员可能负责在与您相同的程序集中构建另一个类。拥有内部会降低抽象的清晰度,并且如果被滥用可能会导致问题。如果您将其公开,这将是相同的问题。其他开发人员正在构建的其他类仍然是消费者,就像任何外部类一样。类抽象和封装不仅仅是为了保护/免受外部类的影响,而是为了任何和所有类。

另一个问题是许多开发人员会认为他们可能需要在程序集的其他地方使用它并将其标记为内部,即使他们当时不需要它。另一位开发人员可能会认为它在那里可供使用。通常,您希望将其标记为私有,直到您有明确的需求。

但是其中一些可能是主观的,我并不是说永远不应该使用它。只需在需要时使用。

于 2008-10-03T04:56:00.083 回答
9

前几天,也许是一周,在一个我不记得的博客上看到了一个有趣的东西。基本上我不能对此表示赞赏,但我认为它可能有一些有用的应用程序。

假设您希望另一个程序集可以看到一个抽象类,但您不希望有人能够从它继承。Sealed 不起作用,因为它是抽象的,该程序集中的其他类确实从它继承。Private 不起作用,因为您可能想在另一个程序集中的某个地方声明一个 Parent 类。

命名空间 Base.Assembly
{
  公共抽象类父
  {
    内部抽象 void SomeMethod();
  }

  //这很好用,因为它在同一个程序集中。
  公共类 ChildWithin : 父级
  {
    内部覆盖 void SomeMethod()
    {
    }
  }
}

命名空间另一个.Assembly
{
  //Kaboom,因为你不能覆盖一个内部方法
  公共类 ChildOutside :父
  {
  }

  公开课测试
  {

    //正好
    私人父母_parent;

    公共测试()
    {
      //还是可以的
      _parent = new ChildWithin();
    }
  }
}

如您所见,它有效地允许某人使用 Parent 类而不能继承。

于 2008-10-03T13:12:14.687 回答
7

当您有方法、类等需要在当前程序集范围内访问时,永远不能在它之外访问。

例如,一个 DAL 可能有一个 ORM,但对象不应暴露给业务层,所有交互都应通过静态方法完成并传入所需的参数。

于 2008-10-03T04:52:57.657 回答
7

此示例包含两个文件:Assembly1.cs 和 Assembly2.cs。第一个文件包含一个内部基类 BaseClass。在第二个文件中,尝试实例化 BaseClass 将产生错误。

// Assembly1.cs
// compile with: /target:library
internal class BaseClass 
{
   public static int intM = 0;
}

// Assembly1_a.cs
// compile with: /reference:Assembly1.dll
class TestAccess 
{
   static void Main()
   {  
      BaseClass myBase = new BaseClass();   // CS0122
   }
}

在此示例中,使用您在示例 1 中使用的相同文件,并将 BaseClass 的可访问性级别更改为public。还将成员 IntM 的可访问性级别更改为internal。在这种情况下,您可以实例化类,但不能访问内部成员。

// Assembly2.cs
// compile with: /target:library
public class BaseClass 
{
   internal static int intM = 0;
}

// Assembly2_a.cs
// compile with: /reference:Assembly1.dll
public class TestAccess 
{
   static void Main() 
   {      
      BaseClass myBase = new BaseClass();   // Ok.
      BaseClass.intM = 444;    // CS0117
   }
}

来源: http: //msdn.microsoft.com/en-us/library/7c5ka91b (VS.80).aspx

于 2010-06-11T00:52:35.957 回答
6

根据经验,有两种成员:

  • 公共表面:从外部程序集中可见(公共、受保护和内部受保护):调用者不受信任,因此需要参数验证、方法文档等。
  • 私有表面:从外部程序集中不可见(私有和内部,或内部类):调用者通常是受信任的,因此可以省略参数验证、方法文档等。
于 2008-10-03T10:59:11.160 回答
6

internal 的一个非常有趣的用途——当然,内部成员仅限于声明它的程序集——在某种程度上从中获得“朋友”功能。朋友成员是仅对声明它的程序集之外的某些其他程序集可见的东西。C# 没有对朋友的内置支持,但 CLR 有。

您可以使用InternalsVisibleToAttribute来声明友元程序集,并且友元程序集中的所有引用都将声明程序集的内部成员视为友元程序集范围内的公共成员。这样做的一个问题是所有内部成员都是可见的。你不能挑挑拣拣。

InternalsVisibleTo 的一个很好的用途是将各种内部成员公开给单元测试程序集,从而消除了对复杂反射工作的需要来测试这些成员。所有内部成员都是可见的并不是什么大问题,但是采用这种方法确实会严重破坏您的类接口,并且可能会破坏声明程序集中的封装。

于 2008-10-03T11:39:05.413 回答
4

降噪,你暴露的类型越少,你的库就越简单。防篡改/安全性是另一个(尽管反射可以战胜它)。

于 2008-10-03T11:02:13.070 回答
4

内部类使您能够限制程序集的 API。这有一些好处,比如让您的 API 更易于理解。

此外,如果您的程序集中存在错误,则修复引入重大更改的可能性较小。如果没有内部类,您将不得不假设更改任何类的公共成员将是一个重大更改。对于内部类,您可以假设修改其公共成员只会破坏程序集的内部 API(以及 InternalsVisibleTo 属性中引用的任何程序集)。

我喜欢在类级别和程序集级别进行封装。有些人不同意这一点,但很高兴知道该功能可用。

于 2010-06-10T16:17:51.943 回答
2

internal 关键字的一种用途是限制程序集用户对具体实现的访问。

如果您有一个工厂或其他一些用于构造对象的中心位置,那么您的程序集的用户只需要处理公共接口或抽象基类。

此外,内部构造函数允许您控制实例化其他公共类的位置和时间。

于 2008-10-03T05:37:46.520 回答
2

我有一个项目使用 LINQ-to-SQL 作为数据后端。我有两个主要的命名空间:Biz 和 Data。LINQ 数据模型位于 Data 中并标记为“内部”;Biz 命名空间有围绕 LINQ 数据类的公共类。

所以有Data.Client, 和Biz.Client; 后者公开了数据对象的所有相关属性,例如:

private Data.Client _client;
public int Id { get { return _client.Id; } set { _client.Id = value; } }

Biz 对象有一个私有构造函数(强制使用工厂方法)和一个内部构造函数,如下所示:

internal Client(Data.Client client) {
    this._client = client;
}

库中的任何业务类都可以使用它,但前端 (UI) 无法直接访问数据模型,从而确保业务层始终充当中介。

这是我第一次真正使用internal很多,并且证明它非常有用。

于 2008-10-03T12:52:08.223 回答
2

在某些情况下,使成员成为类是有意义的internal。例如,如果您想控制类的实例化方式;假设您提供了某种工厂来创建类的实例。您可以创建构造函数internal,以便工厂(位于同一程序集中)可以创建类的实例,但该程序集之外的代码不能。

但是,我看不出没有特定原因创建类或成员有任何意义,就像创建它们或没有特定原因internal一样有意义。publicprivate

于 2010-06-10T16:01:59.377 回答
1

我唯一使用过 internal 关键字的是我的产品中的许可证检查代码;-)

于 2008-10-03T05:32:13.087 回答
1

这个怎么样:通常建议您不要将 List 对象公开给程序集的外部用户,而是公开一个 IEnumerable。但是在程序集中使用 List 对象要容易得多,因为您可以获得数组语法和所有其他 List 方法。因此,我通常有一个内部属性公开要在程序集中使用的列表。

欢迎对这种方法发表评论。

于 2009-12-30T19:18:14.377 回答
1

请记住,public当有人查看您的项目命名空间时,任何定义为的类都会自动显示在智能感知中。从 API 的角度来看,重要的是只向项目的用户显示他们可以使用的类。使用internal关键字隐藏他们不应该看到的东西。

如果您的Big_Important_Classfor Project A 打算在您的项目之外使用,那么您不应该标记它internal

但是,在许多项目中,您通常会拥有实际上只打算在项目内部使用的类。例如,您可能有一个将参数保存到参数化线程调用的类。在这些情况下,您应该将它们标记internal为没有其他原因,只是为了保护自己免受意外的 API 更改的影响。

于 2010-06-10T16:04:34.410 回答
1

这个想法是,当您设计一个库时,只有打算从外部(由您的库的客户)使用的类应该是公共的。这样你就可以隐藏类

  1. 在未来的版本中可能会发生变化(如果它们是公开的,你会破坏客户端代码)
  2. 对客户无用,并可能引起混乱
  3. 不安全(因此使用不当可能会严重破坏您的图书馆)

等等

如果您正在开发内部解决方案而不是使用内部元素,我猜这并不重要,因为通常客户会不断与您联系和/或访问代码。不过,它们对于库开发人员来说相当重要。

于 2010-06-10T16:06:53.360 回答
-7

当您的类或方法不完全适合面向对象范式时,它们会做危险的事情,需要从您控制下的其他类和方法调用,并且您不想让其他人使用.

public class DangerousClass {
    public void SafeMethod() { }
    internal void UpdateGlobalStateInSomeBizarreWay() { }
}
于 2008-10-03T04:49:17.683 回答