您能解释一下C#中internal
关键字的实际用途吗?
我知道internal
修饰符限制了对当前程序集的访问,但在什么情况下应该使用它?
您能解释一下C#中internal
关键字的实际用途吗?
我知道internal
修饰符限制了对当前程序集的访问,但在什么情况下应该使用它?
实用程序或帮助类/方法,您希望从同一程序集中的许多其他类中访问,但您想确保其他程序集中的代码无法访问。
来自MSDN(通过archive.org):
在基于组件的开发中,内部访问的常见用法是使一组组件能够以私有方式协作,而不会暴露给应用程序代码的其余部分。例如,用于构建图形用户界面的框架可以提供Control和Form类,这些类使用具有内部访问权限的成员进行协作。由于这些成员是内部的,因此它们不会暴露给正在使用框架的代码。
您还可以将internal修饰符与InternalsVisibleTo
程序集级别属性一起使用,以创建被授予对目标程序集内部类特殊访问权限的“友好”程序集。
这对于创建允许调用要测试的程序集的内部成员的单元测试程序集非常有用。当然,没有其他程序集被授予这种级别的访问权限,因此在发布系统时,封装性得到了维护。
使用 internal 的另一个原因是,如果您混淆二进制文件,则混淆器知道可以安全地对任何内部类的类名进行混淆,而不能对公共类的名称进行混淆,因为这可能会破坏现有的引用。
如果你正在编写一个将大量复杂功能封装到简单公共API中的DLL,那么“internal”关键字可用于不希望公开的类成员。
隐藏复杂性(即封装)是优质软件工程的主要概念。
当你在构建非托管代码的包装器时,通常会使用internal关键字。
如果你想要DllImport一个基于C/C++库的函数,你可以将这些函数作为类的静态函数导入,并使它们internal,这样用户只能访问你的包装器,而不是原始API,从而避免对程序造成影响。由于这些函数是静态的,所以可以在程序集中的多个包装器类中重复使用。
你可以看一下Mono.Cairo,它是一个使用这种方法的cairo库的包装器。
在“尽可能使用严格的修改器”规则的驱动下,我在需要从另一个类访问方法的任何地方都使用internal,直到我明确需要从另一个程序集访问它。
由于程序集接口通常比其类接口更窄,因此我在许多地方使用它。
我认为过度使用internal是不好的。你不应该将某些功能仅暴露给某些类,而不是其他消费者。
在我看来,这会破坏接口和抽象层。这并不意味着它永远不应该使用,但更好的解决方案是重构为不同的类,或者在可能的情况下以不同的方式使用。然而,并非总是可行。
原因在于另一个开发人员可能负责在与您的程序集相同的程序集中构建另一个类。内部成分减少了抽象的清晰度,并且如果被误用就会引起问题。这与将其设置为public一样。正在由其他开发人员构建的另一个类仍然是消费者,就像任何外部类一样。类的抽象化和封装不仅仅是为了保护/来自外部类,而是适用于所有类。
另一个问题是许多开发人员会认为他们可能需要在程序集的其他地方使用它,并在此时将其标记为internal,即使他们并不需要它。然后,另一个开发人员可能会认为它可以使用。通常情况下,你希望将其标记为private,直到你有明确的需求。
但其中一些因素可能是主观的,我并不是说它永远不能使用。只有在需要时才使用。
这个例子包含两个文件: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
namespace Base.Assembly { public abstract class Parent { internal abstract void SomeMethod(); }如你所见,这种方法可以让别人使用 Parent 类,但不能从它继承。
// 这样做没有问题,因为 ChildWithin 和 Parent 在同一个程序集中。 public class ChildWithin : Parent { internal override void SomeMethod() { } } }
namespace Another.Assembly { // 会出现错误,因为你无法重写一个 internal 方法。 public class ChildOutside : Parent { }
public class Test {
// 没问题 private Parent _parent;
public Test() { // 仍然没问题 _parent = new ChildWithin(); } } }
当您有需要在当前程序集范围内访问但从不在其外部访问的方法、类等时。
例如,一个数据访问层(DAL)可能有一个ORM,但对象不应该暴露给业务层,所有交互都应通过静态方法和传递所需参数来完成。