C#中“internal”关键字的实际用途

问题描述 投票:383回答:22

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

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

c# access-modifiers internals
22个回答
350
投票

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

来自MSDN(通过archive.org):

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

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

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


6
投票

内部的一个非常有趣的用法 - 内部成员当然仅限于声明它的程序集 - 在某种程度上获得了“朋友”功能。友元成员只能在其声明的程序集之外的某些其他程序集中可见。 C#没有内置支持的朋友,但CLR确实如此。

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

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


5
投票

当你有方法,类等需要在当前程序集的范围内可访问而永远不在它之外。

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


5
投票

根据经验法则,有两种成员:

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

4
投票

降噪,您公开的类型越少,您的库就越简单。防篡改/安全是另一个(虽然反射可以赢得反对)。


4
投票

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

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

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


2
投票

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

所以有Data.ClientBiz.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,它证明非常有用。


2
投票

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

但是,我没有看到任何关于制作类或成员internal没有特定原因的意义,只有在没有特定原因使它们成为publicprivate时才有意义。


1
投票

我曾经使用过内部关键字的唯一一件事就是我产品中的许可证检查代码;-)


1
投票

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

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

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


1
投票

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

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


81
投票

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

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

内部的要点不是让鲍勃生活困难。它允许您控制项目A对功能,生命周期,兼容性等所做的昂贵承诺。


1
投票

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

如果项目A的Big_Important_Class打算在项目之外使用,那么你不应该将它标记为internal

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


0
投票

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

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

等等

如果你正在开发内部解决方案而不是使用内部元素我认为并不重要,因为通常客户会与你保持联系和/或访问代码。但它们对于图书馆开发者来说相当重要。


-6
投票

如果您的类或方法不能完全适合面向对象的范例,这些范例会执行危险的东西,需要从您控制的其他类和方法中调用,并且您不希望任何其他人使用它们。

public class DangerousClass {
    public void SafeMethod() { }
    internal void UpdateGlobalStateInSomeBizarreWay() { }
}

57
投票

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


27
投票

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

隐藏复杂性(a.k.a. encapsulation)是高质量软件工程的主要概念。


20
投票

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

如果你有一个你想要DllImport的基于C / C ++的库,你可以将这些函数作为类的静态函数导入,并将它们作为内部函数,这样你的用户只能访问你的包装器而不是原始的API,所以它不能什么搞乱。这些函数是静态的,你可以在程序集中的任何地方使用它们,用于你需要的多个包装类。

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


12
投票

由“尽可能使用严​​格修饰符”规则驱动我使用内部所有我需要从另一个类访问的方法,直到我明确需要从另一个程序集访问它。

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


10
投票

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

在我看来,这打破了界面,打破了抽象。这并不是说它永远不应该被使用,但更好的解决方案是重构到不同的类或者如果可能的话以不同的方式使用。但是,这可能并不总是可行的。

它可能导致问题的原因是另一个开发人员可能被指控在与您相同的程序集中构建另一个类。内部因素会降低抽象的清晰度,如果被滥用会导致问题。这就像你把它公之于众一样。其他开发人员正在构建的另一个类仍然是消费者,就像任何外部类一样。类抽象和封装不仅仅用于保护外部类,也适用于任何和所有类。

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

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


8
投票

前几天,也许是一周,在我不记得的博客上看到一个有趣的。基本上我不能相信这一点,但我认为它可能有一些有用的应用程序。

假设您希望另一个程序集看到一个抽象类,但您不希望某人能够继承它。密封将无法工作,因为它是抽象的原因,该程序集中的其他类确实从它继承。 Private将无法工作,因为您可能希望在另一个程序集中的某处声明Parent类。

namespace Base.Assembly
{
  public abstract class Parent
  {
    internal abstract void SomeMethod();
  }

  //This works just fine since it's in the same assembly.
  public class ChildWithin : Parent
  {
    internal override void SomeMethod()
    {
    }
  }
}

namespace Another.Assembly
{
  //Kaboom, because you can't override an internal method
  public class ChildOutside : Parent
  {
  }

  public class Test 
  { 

    //Just fine
    private Parent _parent;

    public Test()
    {
      //Still fine
      _parent = new ChildWithin();
    }
  }
}

正如您所看到的,它有效地允许某人使用Parent类而无法继承。


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

© www.soinside.com 2019 - 2024. All rights reserved.