请您解释一下C#中internal
关键字的实际用法是什么?
我知道internal
修饰符会限制对当前程序集的访问,但是我应该在何时以及在何种情况下使用它?
答案 0 :(得分:345)
您希望从同一程序集中的许多其他类访问的实用程序或帮助程序类/方法,但是您希望确保其他程序集中的代码无法访问。
来自MSDN(通过archive.org):
内部访问的一个常见用途是基于组件的开发,因为它使一组组件能够以私有方式进行协作,而不会暴露给其他应用程序代码。例如,用于构建图形用户界面的框架可以提供使用具有内部访问权限的成员进行协作的Control和Form类。由于这些成员是内部成员,因此不会暴露给使用该框架的代码。
您还可以使用内部修饰符和InternalsVisibleTo
程序集级别属性来创建“朋友”程序集,这些程序集被授予对目标程序集内部类的特殊访问权限。
这对于创建单元测试程序集非常有用,然后允许它们调用要测试的程序集的内部成员。当然,没有其他程序集被授予此级别的访问权限,因此当您释放系统时,将保留封装。
答案 1 :(得分:81)
如果Bob需要BigImportantClass,那么Bob需要让拥有项目A的人员进行注册,以保证将编写BigImportantClass以满足他的需求,经过测试以确保满足他的需求,并记录为满足他的需求,以及将建立一个流程,以确保永远不会改变,以便不再满足他的需求。
如果一个类是内部的,那么它就不必经过那个过程,这样可以节省他们可以花在其他事情上的项目A的预算。
内部的要点不是让鲍勃生活困难。它允许您控制项目A对功能,生命周期,兼容性等所做的昂贵承诺。
答案 2 :(得分:57)
使用内部的另一个原因是你混淆了你的二进制文件。混淆器知道对任何内部类的类名进行加密是安全的,而公共类的名称不能被扰乱,因为这可能会破坏现有的引用。
答案 3 :(得分:27)
如果您正在编写一个将大量复杂功能封装到简单公共API中的DLL,那么“内部”将用于不会公开公开的类成员。
隐藏复杂性(a.k.a. encapsulation)是高质量软件工程的主要概念。
答案 4 :(得分:20)
当您在非托管代码上构建包装器时,会大量使用internal关键字。
如果你有一个基于C / C ++的库你想要DllImport你可以将这些函数作为类的静态函数导入,并将它们作为内部函数,那么你的用户只能访问你的包装器,而不是原始的API,所以它不能搞乱任何东西。这些函数是静态的,你可以在程序集中的任何地方使用它们,用于你需要的多个包装类。
你可以看看Mono.Cairo,它是使用这种方法的cairo库的包装器。
答案 5 :(得分:12)
由“尽可能使用严格修饰符”规则驱动我使用内部的任何地方我需要访问另一个类的方法,直到我明确需要从另一个程序集访问它。
由于汇编接口通常比其类接口的总和更窄,因此我使用它的地方很多。
答案 6 :(得分:10)
我发现内部被过度使用了。你真的不应该只将某些功能暴露给你不会对其他消费者的某些类别。
这在我看来打破了界面,打破了抽象。这并不是说它永远不应该被使用,但更好的解决方案是重构到不同的类或者如果可能的话以不同的方式使用。但是,这可能并不总是可行。
它可能导致问题的原因是另一个开发人员可能被指控在与您相同的程序集中构建另一个类。内部因素会降低抽象的清晰度,如果被滥用会导致问题。这就像你把它公之于众一样。其他开发人员正在构建的另一个类仍然是消费者,就像任何外部类一样。类抽象和封装不仅仅用于保护外部类,也适用于任何和所有类。
另一个问题是很多开发人员会认为他们可能需要在程序集的其他地方使用它并将其标记为内部,即使他们当时不需要它。然后另一位开发商可能认为它在那里。通常,您希望标记为私有,直到您有一个确定的需求。
但其中一些可能是主观的,我并不是说永远不应该使用它。只需在需要时使用。
答案 7 :(得分: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类而无法继承。
答案 8 :(得分: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的可访问性级别更改为内部。在这种情况下,您可以实例化该类,但无法访问内部成员。
// 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
答案 9 :(得分:6)
内部的一个非常有趣的用法 - 内部成员当然仅限于声明它的程序集 - 在某种程度上获得了“朋友”功能。友元成员只能在其声明的程序集之外的某些其他程序集中可见。 C#没有内置支持的朋友,但CLR确实如此。
您可以使用InternalsVisibleToAttribute来声明朋友程序集,并且友元程序集中的所有引用都会将声明程序集的内部成员视为友元程序集范围内的公共成员。这样做的一个问题是所有内部成员都是可见的;你无法挑选。
InternalsVisibleTo的一个很好的用途是将各种内部成员暴露给单元测试程序集,从而消除了对复杂反射工作的需求以测试这些成员。所有可见的内部成员都不是问题,但采用这种方法会严重破坏类接口,并可能破坏声明程序集中的封装。
答案 10 :(得分:5)
根据经验法则,有两种成员:
答案 11 :(得分:5)
当你有方法,类等需要在当前程序集的范围内访问时,从不在它之外。
例如,DAL可能具有ORM,但不应将对象暴露给业务层,所有交互都应通过静态方法完成,并传递所需的参数。
答案 12 :(得分:4)
降噪,您公开的类型越少,您的库就越简单。 防篡改/安全是另一个(虽然反射可以赢得反对)。
答案 13 :(得分:4)
内部类使您可以限制程序集的API。这样做有一些好处,比如让您的API更易于理解。
此外,如果程序集中存在错误,则修复程序引入重大更改的可能性较小。如果没有内部类,您将不得不假设更改任何类的公共成员将是一个重大变化。对于内部类,您可以假设修改其公共成员只会破坏程序集的内部API(以及InternalsVisibleTo属性中引用的任何程序集)。
我喜欢在类级别和程序集级别进行封装。有些人不同意这一点,但很高兴知道功能可用。
答案 14 :(得分:2)
我有一个项目,它使用LINQ-to-SQL作为数据后端。我有两个主要的命名空间:Biz和Data。 LINQ数据模型存在于数据中并标记为“内部”; 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
,这证明非常有用。
答案 15 :(得分:2)
有些情况下,让班级成员internal
有意义。一个例子可能是你想要控制类的实例化方式;假设您提供某种工厂来创建类的实例。您可以创建构造函数internal
,以便工厂(驻留在同一个程序集中)可以创建类的实例,但该程序集之外的代码不能。
但是,在没有特定原因的情况下制作课程或成员internal
时,我认为没有任何意义,只有在没有特定原因的情况下制作课程public
或private
才有意义的原因。
答案 16 :(得分:1)
internal关键字的一个用途是限制对程序集用户的具体实现的访问。
如果您有一个工厂或其他中心位置来构建对象,那么程序集的用户只需要处理公共接口或抽象基类。
此外,内部构造函数允许您控制实例化其他公共类的位置和时间。
答案 17 :(得分:1)
我唯一使用过内部关键字的是我产品中的许可证检查代码; - )
答案 18 :(得分:1)
这个怎么样:通常建议您不要将List对象公开给程序集的外部用户,而是公开IEnumerable。但是在程序集中使用List对象要容易得多,因为您获得了数组语法和所有其他List方法。因此,我通常有一个内部属性,公开要在程序集中使用的List。
欢迎对这种方法发表评论。
答案 19 :(得分:1)
请记住,当有人查看您的项目名称空间时,任何定义为public
的类都会自动显示在intellisense中。从API的角度来看,重要的是只向项目用户显示他们可以使用的类。使用internal
关键字隐藏他们看不到的内容。
如果项目A的Big_Important_Class
打算在项目之外使用,则不应将其标记为internal
。
但是,在许多项目中,您通常会拥有真正仅用于项目内部的类。例如,您可能有一个类,其中包含参数化线程调用的参数。在这些情况下,如果没有其他原因,您应将其标记为internal
,而不是保护自己免受意外的API更改。
答案 20 :(得分:0)
这个想法是,当你设计一个库时,只有那些打算在外面使用的类(由你的库的客户端)应该是公共的。这样你就可以隐藏那些
的类等
如果您正在开发内部解决方案而不是使用内部元素我认为并不重要,因为通常客户将与您保持持续联系和/或访问代码。但它们对于图书馆开发者来说相当重要。
答案 21 :(得分:0)
答案 22 :(得分:-6)
当你的类或方法不能完全适合面向对象的范例时,它会做危险的事情,需要从你控制的其他类和方法中调用,而你不想让它们其他人都在使用。
public class DangerousClass {
public void SafeMethod() { }
internal void UpdateGlobalStateInSomeBizarreWay() { }
}