能否解释一下,internal
C#中关键字的实际用途是什么?
我知道internal
修饰符限制了对当前程序集的访问,但我什么时候需要呢?
您希望从同一程序集中的许多其他类访问的实用程序或帮助程序类/方法,但您希望确保其他程序集中的代码无法访问.
来自MSDN(通过archive.org):
内部访问的一个常见用途是基于组件的开发,因为它使一组组件能够以私有方式进行协作,而不会暴露给其他应用程序代码.例如,用于构建图形用户界面的框架可以提供使用具有内部访问权限的成员进行协作的Control和Form类.由于这些成员是内部成员,因此它们不会暴露给使用该框架的代码.
您还可以使用内部修饰符和InternalsVisibleTo
程序集级别属性来创建"朋友"程序集,这些程序集被授予对目标程序集内部类的特殊访问权限.
这对于创建单元测试程序集非常有用,然后允许它们调用要测试的程序集的内部成员.当然,没有其他程序集被授予此级别的访问权限,因此当您释放系统时,将保留封装.
如果Bob需要BigImportantClass,那么Bob需要让拥有项目A的人员注册以保证BigImportantClass将被编写以满足他的需求,经过测试以确保满足他的需求,记录为满足他的需求,以及流程将落实到位,以确保永远不会改变,以便不再满足他的需要.
如果一个类是内部的,那么它不必经过该过程,这节省了他们可以花在其他事情上的项目A的预算.
内部的要点不是让鲍勃生活困难.它允许您控制项目A对功能,生命周期,兼容性等所做的昂贵承诺.
使用内部的另一个原因是你混淆了你的二进制文件.混淆器知道对任何内部类的类名进行加密是安全的,而公共类的名称不能被加扰,因为这可能会破坏现有的引用.
如果您正在编写一个将大量复杂功能封装到一个简单公共API中的DLL,那么"内部"将用于不公开公开的类成员.
隐藏复杂性(又称封装)是高质量软件工程的主要概念.
在构建非托管代码的包装器时,会大量使用internal关键字.
如果你有一个你想要DllImport的基于C/C++的库,你可以将这些函数作为类的静态函数导入,并将它们作为内部函数,这样你的用户只能访问你的包装器而不是原始的API,所以它不能什么搞乱.这些函数是静态的,你可以在程序集中的任何地方使用它们,用于你需要的多个包装类.
你可以看看Mono.Cairo,它是使用这种方法的cairo库的包装器.
由"尽可能使用严格修饰符"规则驱动我使用内部所有我需要从另一个类访问的方法,直到我明确需要从另一个程序集访问它.
由于汇编接口通常比其类接口的总和更窄,因此我使用它的地方很多.
我发现内部被过度使用了.你真的不应该只将某些功能暴露给你不会对其他消费者的某些类.
在我看来,这打破了界面,打破了抽象.这并不是说它永远不应该被使用,但更好的解决方案是重构到不同的类或者如果可能的话以不同的方式使用.但是,这可能并不总是可行的.
它可能导致问题的原因是另一个开发人员可能被指控在与您相同的程序集中构建另一个类.内部因素会降低抽象的清晰度,如果被滥用会导致问题.这就像你把它公之于众一样.其他开发人员正在构建的另一个类仍然是消费者,就像任何外部类一样.类抽象和封装不仅仅用于保护外部类,也适用于任何和所有类.
另一个问题是很多开发人员会认为他们可能需要在程序集的其他地方使用它并将其标记为内部,即使他们当时并不需要它.然后另一位开发商可能认为它在那里.通常,您希望标记为私有,直到您有一个确定的需求.
但其中一些可能是主观的,我并不是说永远不应该使用它.只需在需要时使用.
前几天,也许是一周,在我不记得的博客上看到一个有趣的.基本上我不能相信这一点,但我认为它可能有一些有用的应用程序.
假设您希望另一个程序集看到一个抽象类,但您不希望某人能够继承它.密封将无法工作,因为它是抽象的原因,该程序集中的其他类确实从它继承.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类而无法继承.
此示例包含两个文件: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
内部的一个非常有趣的用法 - 内部成员当然仅限于声明它的程序集 - 在某种程度上获得了"朋友"功能.友元成员只能在其声明的程序集之外的某些其他程序集中可见.C#没有内置支持的朋友,但CLR确实如此.
您可以使用InternalsVisibleToAttribute来声明朋友程序集,并且友元程序集中的所有引用都会将声明程序集的内部成员视为友元程序集范围内的公共成员.这样做的一个问题是所有内部成员都是可见的; 你不能挑选.
InternalsVisibleTo的一个很好用途是将各种内部成员暴露给单元测试组件,从而消除了对复杂反射工作的需求以测试这些成员.所有内部成员都可见并不是一个问题,但采用这种方法会严重破坏类接口,并可能破坏声明程序集中的封装.
当你有方法,类等需要在当前程序集的范围内可访问而永远不在它之外.
例如,DAL可能具有ORM但对象不应暴露给业务层,所有交互都应通过静态方法完成并传入所需的参数.
根据经验法则,有两种成员:
公共表面:从外部程序集中可见(公共,受保护和内部受保护):调用者不受信任,因此需要参数验证,方法文档等.
私有表面:从外部程序集(私有和内部或内部类)不可见:调用程序通常是可信的,因此可以省略参数验证,方法文档等.