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

Ale*_*yev 402 c# internals access-modifiers

能否解释一下,internalC#中关键字的实际用途是什么?

我知道internal修饰符限制了对当前程序集的访问,但我什么时候需要呢?

Ash*_*Ash 360

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

来自MSDN(通过archive.org):

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

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

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

  • 我的感觉是,从你需要内部的那一刻起,某个地方的设计就出现了问题.恕我直言,一个应该能够使用纯OO来解决所有问题.在这个时刻,我正在盯着.NET Framework源代码中第一百万次内部使用,禁止我利用他们自己使用的东西.通过使用内部,他们创建了一个表面上模块化的整体,但是当你试图隔离东西时会崩溃.此外,安全不是一个争论,因为有救援的反映. (31认同)
  • @GrimaceofDespair:让我在这里添加一个反对意见.我个人认为内部是大型多项目解决方案中"公共"修饰符的一个非常有用的版本.将此修饰符添加到子项目中的类中会禁止解决方案中的其他子项目"随机使用"此类,这样我就可以强制正确使用我的库.如果我稍后需要进行一些重构,我就不必问自己"等等,但是为什么那个人去创建一个Point类的实例,我在这个特定的计算中只需要我自己的目的"? (26认同)
  • 我认为内部比公共更有用.你唯一一次使用public就是当你明确说"在这里,用户 - 我正在为你提供一个有用的功能".如果您正在编写应用程序而不是用户库,那么所有内容都应该是内部的,因为您不会尝试向人们提供任何外部调用功能.如果您正在编写用户库,那么只有您希望提供给用户的有用功能,维护,支持和保持到最后才能公开.否则,将其设为内部. (13认同)
  • InternalsVisibleTo属性是一个很好的帮助.谢谢. (8认同)
  • 换句话说,如果每个人都依赖于SmtpClient的内部,并且在某个时刻发现了一个bug,那么.NET会有很多问题需要修复这个bug,甚至可能会长时间保留它.我记得我曾经读过一篇有趣的文章,描述了Windows开发人员为了与使用各种内部功能和错误的所有旧程序保持"兼容性"的长度.用.NET重复此错误没有意义. (3认同)
  • 总的来说,我同意你的看法.唯一的例外是能够构建单元测试而无需创建私有访问器.如果要检查是否正确分配了特定依赖项,并且包含依赖项的字段不可公开访问,则使用internal是可行的方法. (2认同)
  • @Nyerguds,我仍然认为实现者标记为内部的黑客类在这里不是一个好方法。一旦执行此操作,便会进入危险区域,在该危险区域中,如果对正在使用的库进行其他兼容的更新,将会意外地且不可预测地破坏系统。在您的情况下,一种合理的方法是将要修复的类作为子模块复制到自己的代码库中,并根据自己的喜好进行修复,并根据需要管理内部。(尽管我不确定,.NET许可证允许。) (2认同)
  • @KT.是的,我猜你有一点意思.但我仍然认为它经常会阻止超过它需要的东西. (2认同)

Eri*_*ert 83

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

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

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

  • @cwallenpoole:如果你正在使用由编写你不喜欢的代码的骗子编写的代码,那么就停止使用他们的代码并编写自己喜欢的代码. (10认同)
  • @Adam:可修改、可扩展和可定制的代码是一个需要花费*金钱*的*功能*。你不能通过公开一切来免费获得它,因为这会导致更多问题;现在,该库不再维护任何不变量,违反不变量的行为称为“错误”。如果您与图书馆供应商有问题,请向该供应商投诉,而不是向我投诉。 (6认同)
  • 有一个完美的感觉.只是希望我们能够覆盖内部成员,因为我们都在这里同意成年人. (4认同)
  • @EricLippert不是我的意思.如果我继承了其中包含"内部"的已编译代码,我就被卡住吧?没有办法简单地告诉编译器"不,其他开发人员对你撒谎.你应该相信我",除非我使用反射. (4认同)
  • @cwallenpoole:我至少没有被冒犯.如果你发现自己陷入了这种不幸的境地,我会提供很好的建议. (4认同)
  • @EricLippert 今天我使用的是供应商提供的库(他们拥有自己的平台 - 数百万行 C# 代码),其中一位开发人员选择在几个地方使用内部,显然是因为这是“最佳实践”。现在没有人可以修改、扩展、定制他们的代码。“寻找新的供应商和操作系统”并不是一个明智的选择。“迫使每家公司雇用更高技能的程序员”同上。在实践中,“内部”是不好的做法*除非运行时提供了一种强制覆盖它的方法*(目前还没有:()。(虽然我确实喜欢+使用它,但它很少被正确使用IME) (3认同)
  • @EricLippert 那应该是口是心非,我没有冒犯的意思。(在这种情况下,我主要是想确认我是 SOL)。 (2认同)

Joe*_*ler 57

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

  • 所以你睡觉时不要锁门,因为你从来没有听说过一个被锁定的小偷? (24认同)
  • 这就是我在源代码中打乱类名和变量名的原因。您也许能够弄清楚 PumpStateComparator 是什么,但是您能猜出 LpWj4mWE 是什么吗?#jobsecurity(我不这样做,请不要真的这样做,互联网人。) (7认同)
  • PFF。用反汇编器查看字节码仍然可以很清楚地知道代码的作用。混淆器对真正试图进入内部的人几乎没有温和的威慑作用。从来没有听说过黑客因为没有得到任何有用的函数名称而停止。 (2认同)

Jef*_*dge 27

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

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


Edw*_*vis 20

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

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

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


Ily*_*hin 12

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

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

  • 由于private必须在类外部访问类属性/方法时过于严格,并且必须从另一个类访问它们的情况,因此bly不常见. (6认同)
  • 如果你"尽可能使用严​​格修饰语"为什么不私有呢? (3认同)

mat*_*ant 10

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

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

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

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

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


Pro*_*ool 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
   }
}
Run Code Online (Sandbox Code Playgroud)

在此示例中,使用您在示例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
   }
}
Run Code Online (Sandbox Code Playgroud)

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


cfe*_*uke 6

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

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

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


Aar*_*ell 5

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

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


Mic*_*tov 5

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

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


Qui*_*ome 5

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