C#中“内部”关键字的实际用法
你能解释一下,C#中的internal
关键字有什么实际用途?
我知道internal
修饰符限制对当前程序集的访问,但是什么时候可以使用?
您希望从相同程序集中的许多其他类访问的实用程序或帮助程序类/方法,但希望确保其他程序集中的代码无法访问。
来自MSDN :
内部访问的一个常见用途是在基于组件的开发中,因为它使得一组组件能够以私有方式进行协作,而不会暴露给其他应用程序代码。 例如,用于构buildgraphics用户界面的框架可以提供使用内部访问的成员进行合作的Control和Form类。 由于这些成员是内部的,所以他们不会暴露于使用框架的代码。
您还可以使用内部修饰符以及InternalsVisibleTo
程序集级别属性来创build被授予对目标程序集内部类的特殊访问权的“朋友”程序集。
这可以用于创buildunit testing程序集,然后允许调用程序集的内部成员进行testing。 当然,没有其他的程序集被授予这个级别的访问权限,所以当你释放你的系统时,封装是保持的。
如果Bob需要BigImportantClass,那么Bob需要让拥有项目A的人员注册,以确保BigImportantClass将被写入以满足他的需求,经过testing以确保满足他的需求,logging为满足他的需求,将落实到位,确保永不改变,不再满足他的需求。
如果一个class级是内部的,那么就不需要经过这个过程,这样可以节省项目A的预算,使他们可以花在其他方面。
内在的一点并不是让鲍勃生活困难。 这就是它允许你控制项目A对特性,寿命,兼容性等方面的昂贵承诺。
使用内部的另一个原因是如果你混淆你的二进制文件。 混淆器知道争夺任何内部类的类名是安全的,而公共类的名称不能被混淆,因为这可能会破坏现有的引用。
如果你正在编写一个将大量复杂function封装到一个简单的公共API中的DLL,那么在类的成员上就会使用“internal”,而这些内部成员将不会被公开暴露。
隐藏复杂性(aka encapsulation)是高质量软件工程的主要概念。
在构build非托pipe代码的包装时,内部关键字被大量使用。
当你有一个基于C / C ++的库,你想DllImport你可以导入这些函数作为一个类的静态函数,并使其内部,所以你的用户只能访问你的包装,而不是原始的API,所以它不能乱七八糟的东西 函数是静态的,你可以在程序集中的任何地方使用它们,用于你需要的多个包装类。
你可以看看Mono.Cairo,这是一个围绕cairo库的包装,它使用这种方法。
由于“尽可能使用严格修饰符”的规则,我使用内部任何地方都需要从另一个类访问方法,直到我明确需要从另一个程序集访问它。
由于程序集接口通常比其类接口的总和更窄,我使用它的地方相当多。
我发现内部被过度使用。 你真的不应该将某些function暴露给某些你不想让其他消费者的function。
这在我看来打破了界面,打破了抽象。 这并不是说它永远不会被使用,但更好的解决scheme是重构到不同的类,或者如果可能的话以不同的方式使用。 但是,这可能不总是可能的。
它可能导致问题的原因是,另一个开发人员可能会被要求在你的同一个程序集中build立另一个类。 内部结构减less了抽象的清晰度,并且如果被滥用会导致问题。 如果你把它公之于众,那也是一样的问题。 其他开发者正在构build的另一个类仍然是一个消费者,就像任何外部类一样。 类抽象和封装不仅仅是为了保护外部类,而是为了任何和所有的类。
另一个问题是,很多开发人员会认为他们可能需要在程序集的其他地方使用它,并将其标记为内部,即使他们当时不需要它。 另一位开发人员则可能会考虑在那里采取。 通常情况下,你想标记为私人,直到你有defintive的需求。
但是其中的一些可能是主观的,我不是说它永远不能被使用。 只需要时使用。
有一天,也许一个星期,在一个我不记得的博客上看到了一个有趣的东西。 基本上我不能赞扬这一点,但我认为它可能有一些有用的应用程序。
假设你想要一个抽象类被另一个程序集看到,但是你不希望某个人能够inheritance它。 密封将无法工作,因为它是抽象的原因,其他类在该大会从它inheritance。 Private不起作用,因为您可能想要在另一个程序集中的某个位置声明一个Parent类。
名称空间Base.Assembly { 公共抽象类父 { 内部抽象void SomeMethod(); } //这个工作正常,因为它在同一个程序集中。 公共类ChildWithin:父 { 内部覆盖void SomeMethod() { } } } 命名空间Another.Assembly { // Kaboom,因为你不能重写一个内部方法 公开课ChildOutside:家长 { } 公共类testing { //正好 私人家长_父母; public Test() { //还是很好 _parent = new ChildWithin(); } } }
正如你所看到的,它实际上允许有人使用父类而不能inheritance。
内部的一个非常有趣的用法 – 当然内部成员仅限于宣称的程序集 – 在某种程度上获得“朋友”function。 朋友成员是只有在其声明的程序集之外的其他程序集才可见的东西。 C#没有内置的朋友支持,但CLR呢。
您可以使用InternalsVisibleToAttribute来声明朋友程序集,并且朋友程序集中的所有引用将在朋友程序集的作用域内将声明程序集的内部成员视为公共。 与此有关的一个问题是所有内部成员都是可见的; 你不能挑选。
InternalsVisibleTo的一个很好的用法就是将不同的内部成员公开给一个unit testing程序集,从而消除了testing这些成员的复杂reflection工作的需求。 所有可见的内部成员都不是什么大问题,但是采取这种方法会严重地影响你的类接口,并且可能会破坏声明程序集中的封装。
当你有方法,类,等等,需要在当前程序集的范围内访问,而不是在它之外。
例如,一个DAL可能有一个ORM,但是这些对象不应该暴露给业务层,所有的交互都应该通过静态方法完成,并且传入所需的参数。
减less噪音,你揭露的types越less,你的图书馆就越简单。 防篡改/安全是另一个(虽然reflection可以赢得反对)。
作为经验法则,有两种成员:
- 公共表面 :从外部程序集(public,protected,and internal protected)可见:调用者不可信,所以需要参数validation,方法文档等。
- 私有表面 :从外部程序集(私有和内部类或内部类)不可见:调用者通常是可信的,因此可以省略参数validation,方法文档等。
这个例子包含两个文件: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的可访问性级别更改为内部 。 在这种情况下,您可以实例化该类,但不能访问内部成员。
// 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
我有一个项目,使用LINQ到SQL数据后端。 我有两个主要的命名空间:商务和数据。 LINQ数据模型存在于Data中并被标记为“internal”; 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
,这是非常有用的。
有些情况下,让class级成员internal
是有意义的。 一个例子可能是如果你想控制如何实例化类; 假设你提供了一些工厂来创build类的实例。 您可以使构造函数成为internal
函数,以便工厂(驻留在同一个程序集中)可以创build该类的实例,但该程序集外部的代码不能。
但是,我看不出有什么意义,没有特定的理由让class级或成员internal
成员,只要没有特定的理由就可以让他们public
或private
。
内部类使您能够限制程序集的API。 这有好处,比如让你的API简单易懂。
另外,如果组件中存在一个错误,修复引入突变的机会就越小。 没有内部class级,你就不得不假设改变任何class级的公共成员将是一个突破性的变化。 对于内部类,可以假定修改其公共成员只会破坏程序集的内部API(以及InternalsVisibleTo属性中引用的任何程序集)。
我喜欢在课堂上和大会上进行封装。 有一些人不同意这一点,但很高兴知道这个function是可用的。
我唯一使用内部关键字的是我的产品中的许可证检查代码;-)
内部关键字的一个用途是限制程序集用户对具体实现的访问。
如果您有一个工厂或其他中心位置来构build对象,则您的程序集的用户只需要处理公共接口或抽象基类。
此外,内部构造函数允许您控制实例化其他公共类的位置和时间。
这个怎么样:通常build议您不要将一个List对象暴露给程序集的外部用户,而是暴露一个IEnumerable。 但是在程序集中使用一个List对象要容易得多,因为你得到了数组的语法和所有其他的List方法。 所以,我通常有一个内部属性暴露了一个List在组件中使用。
我们欢迎评论这种方法。
请记住,当有人看着你的项目命名空间时,任何定义为public
类都将自动显示在intellisense中。 从API的angular度来看,只显示项目的用户可以使用的类是很重要的。 使用internal
关键字来隐藏他们不应该看到的东西。
如果项目A的Big_Important_Class
用于项目之外,则不应将其标记为internal
。
但是,在许多项目中,您经常会有仅仅用于项目内部的类。 例如,您可能有一个保存参数化线程调用参数的类。 在这些情况下,如果没有其他原因,您应该将其标记为internal
,而不是为了保护您自己免于意外的API更改。
这个想法是,当你devise一个图书馆时,只有从外部(由你的图书馆的客户)使用的类应该是公开的。 这样你可以隐藏类
- 将来的版本可能会发生变化(如果它们是公开的,你会破坏客户端代码)
- 对客户无用,可能会造成混淆
- 不安全(如此不当的使用可能会严重破坏你的图书馆)
等等
如果您正在开发内部解决scheme,而不是使用内部元素,那么我想,因为通常客户端会经常与您联系和/或访问代码。 不过,它们对于图书馆开发者来说是相当关键
当你的类或方法不能很好地适应面向对象的范式时,它们会做出危险的东西,这些东西需要在你控制下的其他类和方法中调用,而你不想让其他人使用。
public class DangerousClass { public void SafeMethod() { } internal void UpdateGlobalStateInSomeBizarreWay() { } }