C#访问修饰符internal的访问范围误区释疑

一、前言

MSDN关于访问修饰符的访问级别解释:

访问修饰符是一些关键字,用于指定声明的成员或类型的可访问性。本节介绍四个访问修饰符:

public

protected

internal

private

使用这些访问修饰符可指定下列五个可访问性级别:

public:访问不受限制。

protected:访问仅限于包含类或从包含类派生的类型。

Internal:访问仅限于当前程序集。

protected internal:访问限制到当前程序集或从包含派生的类型的类别。

private:访问仅限于包含类型。

二、认知误区

internal,英文含义是“内部的”,这时候基础不扎实的同学,可能就混淆了这个“内部的”的确切含义,到底是指“同一命名空间”的内部,还是“同一程序集”的内部,本人就是突然被问到这个问题的时候,真的就犹豫了,而且曾经一度以为就是“同一命名空间”的内部(话外:我们太多时候都自以为了,其实只要自己稍微MSDN查一下,就很清楚了)。

第一部分已经摘抄了微软MSDN官方的解释,其实这个内部就是“同一程序集”的内部,也就是说,internal修饰的方法或者属性,只要是在同一个程序集的中的其他类都可以访问,如果二者不在同一命名空间,只要使用using引用上相应的命名空间即可,这里,从另外一个方面也间接看出命名空间并不是界定访问级别的,而是保证全局的类唯一性的,下面就从现实生活中解释下internal的实际作用。

三、释疑

都说艺术源于生活,编程也是一门艺术,所以一样也是可以成生活中找到相应的场景,下面我们以实际生活场景来描述internal修饰符的作用。

某公司的某技术中心,发文规定即日起,中心内的“打印机”仅限本中心的各个部门使用,其他中心的人员不能使用。这个现实生活的场景中,技术中心其实就是个程序集,而每个部门相当于不同的类,当然部门可以有相应的标签,相当于不同的命名空间,其实就是进行逻辑划分,职责不同的部门属于不同的命名空间就可以理解了。而我们的“主角”打印机的旁边就会被贴上公告“本中心的打印机只能本中心使用。。。。”,此时就相当于给打印机打上了internal修饰符了。其他中心的同学们,再也用不上本中心的打印机了。

下面我们用代码来描述上面的场景:

我们先在一个公共的类库中(程序集)定义一个打印机的类

namespace CommonAsset
{
    public class Printer
    {
        private bool _isBad;
        /// <summary>
        /// 打印机是否坏了
        /// </summary>
        public bool IsBad
        {
            get { return _isBad; }
            set { _isBad = value; }
        }

        public void Print()
        {
            Console.WriteLine("开始打印!");
        }
    }
}

然后我们在创建一个中心A(程序集),分别定义各个部门,且属于不同的命名空间

namespace CenterA.Administration
{
    public class DepartmentB
    {
        internal Printer PrinterB { get; set; }
        public DepartmentB()
        {
            PrinterB = new Printer();
        }
    }
}


namespace CenterA.Technology
{
    public class DepartmentA
    {
        internal Printer PrinterA { get; set; }
        public DepartmentA()
        {
            PrinterA = new Printer();
        }

        public void PrintSomething()
        {
            if (PrinterA.IsBad)
            {
                CenterA.Administration.DepartmentB departmentB = new CenterA.Administration.DepartmentB();
                departmentB.PrinterB.Print();
            }
            else
            {
                PrinterA.Print();
            }
        }
    }
}

上面可以看见,DepartmentBDepartmentA虽然不在同一个命名空间,但由于同属于CenterA这个程序集,DepartmentA是可以访问DepartmentB中的Printer 的。


迷惑的internal

程序集和命名空间的关系:一个程序集内可能有多个命名空间,一个命名空间也可能存在于不同的程序集中。由此可知,这两者没有实质的联系,他们只是一个类型在两种层面上的划分(逻辑层面和物理层面),对于编译器来说,命名空间仅仅是在类型名称前加了一些由点号隔开的符号而已,由此来更精确的指定一个类型,防止系统中发生冲突;然而,对于一个类型的定义是发生在程序集中的当查找一个类型的定义时,C#的using指示符会指示编译器试着在类型名上添加不同的前缀,编译器必须被告知到哪些程序集中进行查找,编译器将扫描它知道的所有程序集来查找类型的定义,一旦编译器找到了正确的程序,程序集信息和类型信息会被添加到生成托管模块的元数据中。

综上所述,internal的作用范围是同一程序集而并不是同一命名空间。


清晰程序集和命名空间的关系

命名空间允许我们对相关的类型进行逻辑上的组织,这使得我们很方便的地定位一个类型:
举个简单的例子,我们实例化的一个队列,
如果不引入命名空间,则要这样写:

System.Collections.Queue q=new System.Collections.Queue();

那引入命名空间呢? 则只要这样写:

Using System.Collections;//引入命名空间
Queue q=new Queue();

对于编辑来说,命名空间仅仅是在类型名称前加了一些由点号隔开的符号而已.这使得一个类型的名称更长,从而也更具惟一性.如果两个相同的类在同一个命名空间则会冲突,如果不同的命名空间有相同的类型,也会产生二义性,就像windows中的目录一样,同一目录不能有同名文件,不同目录则可以.如果你非要用,可以用全称(System.Collections.Queue),也可以using sysQueue= System.Collections.Queue;

注意,C#的using指示符会指示编译器试着在类型名上添加不同的前缀,直到找到一个匹配为止,刚才已经讲了,命名空间只是逻辑上,真正的类型在程序集里,当查找一个类型的定义时,编译器必须被告知到哪些程序集中进行查找,编译器将扫描它知道的所有程序集来查找类型的定义.一旦编译器找到了正确的程序,程序集信息和类型信息会被添加到生成托管模块的元数据中,C#编译器默认情况下会自动在MSCorLib.dll程序集中进行查找.而MSCorLib.dll包含了所有FCL中定义的核心类型.例如Object,String等。

命名空间和程序集的关系:

  1. 从属于一个命名空间的各个类型可能在不同的程序集中实现。例如:system.IO.FileStream类型是在MSCorlib.dll程序集中实现的,而System.IO.FileSystemWatcher类型是在System.dll 程序集中实现的。事实上, .Net framework 甚至根本没有发布一个System.IO.dll程序集。(一个程序集内可能有多个命名空间,同一个命名空间可能存在于不同的程序集中.)
  2. 在一个程序集中,也可能包含不同命名空间中的类型。例如,System.Int32System.Text.StringBuilder类型都在MSCorLib.dll程序集中。
  3. 在.net framework SDk文档中查找一个类型时,文档会明确的指出类型所属的命名空间,以及实现了该类型的程序集。 (用一个形象的比喻:一个类型,它有两个身份证,一个是命名空间,一个是程序集)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值