深入浅出OOP(五): C#访问修饰符(Public/Private/Protected/Internal/Sealed/Constants)

深入浅出OOP(五): C#访问修饰符(Public/Private/Protected/Internal/Sealed/Constants)

访问修饰符(或者叫访问控制符)是面向对象语言的特性之一,用于对类、类成员函数、类成员变量进行访问控制。同时,访问控制符也是语法保留关键字,用于封装组件。

Public, Private, Protected at Class Level

在创建类时,我们需要考虑类的作用域范围,如谁可访问该类,谁可访问该类成员变量,谁可访问该类成员函数。 换而言之,我们需要约束类成员的访问范围。一个简单的规则,类成员函数、类成员变量之间可以*

访问不受约束,这里主要说的是外部的访问约束。在创建class的时候,默认的访问控制符为private。

下面做个小实验,打开Visual Studio,创建一个C#的Console应用,命名为AccessModifiers。 添加一个类,命名为Modifiers ,拷贝如下代码:

  1: using System;
  2:
  3: namespace AccessModifiers
  4: {
  5:     class Modifiers
  6:     {
  7:         static void AAA()
  8:         {
  9:             Console.WriteLine("Modifiers AAA");
 10:         }
 11:
 12:         public static void BBB()
 13:         {
 14:             Console.WriteLine("Modifiers BBB");
 15:             AAA();
 16:         }
 17:     }
 18:
 19:      class Program
 20:     {
 21:         static void Main(string[] args)
 22:         {
 23:             Modifiers.BBB();
 24:         }
 25:     }
 26:    }

上面的代码创建了一个类Modifiers,它有2个static函数:AAA、BBB。其中BBB是public访问修饰符,在Main中调用BBB结果如下:

Modifiers BBB

Modifiers AAA

BBB被标记为public,既任何函数皆可访问和运行。AAA被标记为private,既AAA仅能被其类内函数访问,外包是无法访问的。

修改代码如下:

  1: class Program
  2:     {
  3:         static void Main(string[] args)
  4:         {
  5:             Modifiers.AAA();
  6:             Console.ReadKey();
  7:         }
  8:     }

则运行报错:

'AccessModifiers.Modifiers.AAA()' is inaccessible due to its protection level

Modifiers

下面我们对AAA进行重构,修改如下:

  1: class Modifiers
  2:     {
  3:         protected static void AAA()
  4:         {
  5:             Console.WriteLine("Modifiers AAA");
  6:         }
  7:
  8:         public static void BBB()
  9:         {
 10:             Console.WriteLine("Modifiers BBB");
 11:             AAA();
 12:         }
 13:     }
 14:
 15:     class Program
 16:     {
 17:         static void Main(string[] args)
 18:         {
 19:             Modifiers.AAA();
 20:             Console.ReadKey();
 21:         }
 22:     }

运行结果:

'AccessModifiers.Modifiers.AAA()' is inaccessible due to its protection level

既,protected修饰符的成员变量,仅能被其同类、子类访问,外部无法访问。

继承修改

我们接着添加子类,来扩展这个实例:

  1: class ModifiersBase
  2:     {
  3:         static void AAA()
  4:         {
  5:             Console.WriteLine("ModifiersBase AAA");
  6:         }
  7:         public static void BBB()
  8:         {
  9:             Console.WriteLine("ModifiersBase BBB");
 10:         }
 11:         protected static void CCC()
 12:         {
 13:             Console.WriteLine("ModifiersBase CCC");
 14:         }
 15:     }
 16:
 17:   class ModifiersDerived:ModifiersBase
 18:     {
 19:         public static void XXX()
 20:         {
 21:             AAA();
 22:             BBB();
 23:             CCC();
 24:         }
 25:     }
 26:
 27:  class Program
 28:     {
 29:         static void Main(string[] args)
 30:         {
 31:             ModifiersDerived.XXX();
 32:             Console.ReadKey();
 33:         }
 34:     }

运行结果:

'AccessModifiers.ModifiersBase.AAA()' is inaccessible due to its protection level

原因是AAA默认为Private访问控制符,仅可在基类中访问,子类无法访问。

类级别的Internal 修饰符

换另外一个场景,用Visual Studio新建一个dll类库AccessModifiersLibrary,添加一个ClassA类,标记为iternal修饰符,代码如下:

  1: AccessModifiersLibrary.ClassA:
  2:
  3: namespace AccessModifiersLibrary
  4: {
  5:     internal class ClassA
  6:     {
  7:     }
  8: }

编译后,会在~\AccessModifiersLibrary\bin\Debug下找到这个dll。 在Program.cs使用这个dll, 添加dll引用,添加命名空间:

  1: using AccessModifiersLibrary;
  2:
  3: namespace AccessModifiers
  4: {
  5:     class Program
  6:     {
  7:         static void Main(string[] args)
  8:         {
  9:             ClassA classA;
 10:         }
 11:     }
 12: }

编译代码,运行结果如下:

Compile time error: 'AccessModifiersLibrary.ClassA' is inaccessible due to its protection level

之所以报错,是因为internal 修饰符的作用域。internal 修饰符仅对当前程序集(dll 或 exe)内有效,因此,当class添加internal修饰符则意味着程序集外无法访问。

命名空间的修饰符

我们尝试给命名空间添加修饰符,代码如下:

  1: public namespace AccessModifiers
  2: {
  3:     class Program
  4:     {
  5:         static void Main(string[] args)
  6:         {
  7:
  8:         }
  9:     }
 10: }

运行报错。

Compile time error: A namespace declaration cannot have modifiers or attributes

结论,我们无法对命名空间添加修饰符,命名空间默认是public的作用域。

私有类

修改如下代码:

  1:  namespace AccessModifiers
  2: {
  3:     private class Program
  4:     {
  5:         static void Main(string[] args)
  6:         {
  7:
  8:         }
  9:     }
 10: }

编译报错:

Compile time error: Elements defined in a namespace cannot be explicitly declared as private, protected, or protected internal

类可被修饰为public、internal,它无法被标记为protected或者private。类默认的修饰符为internal。

重构代码如下:

  1:  namespace AccessModifiers
  2: {
  3:     public class Program
  4:     {
  5:         static void Main(string[] args)
  6:         {
  7:         }
  8:
  9:         public private void Method1()
 10:         {
 11:
 12:         }
 13:     }
 14: }

编译运行:

Compile time error: More than one protection modifier

结论,修饰符不支持嵌套。既每次仅能用一个修饰符。

Internal 类和Public成员函数

重构代码:

  1: namespace AccessModifiersLibrary
  2: {
  3:     internal class ClassA
  4:     {
  5:         public void MethodClassA(){}
  6:     }
  7: }
  8:
  9: using AccessModifiersLibrary;
 10:
 11:  namespace AccessModifiers
 12: {
 13:     public class Program
 14:     {
 15:         public static void Main(string[] args)
 16:         {
 17:             ClassA classA = new ClassA();
 18:             classA.MethodClassA();
 19:         }
 20:     }
 21: }

运行结果:

'AccessModifiersLibrary.ClassA' is inaccessible due to its protection level The type 'AccessModifiersLibrary.ClassA' has no constructors defined 'AccessModifiersLibrary.ClassA' is inaccessible due to its protection level 'AccessModifiersLibrary.ClassA' does not contain a definition for 'MethodClassA' and no extension method 'MethodClassA' accepting a first argument of type 'AccessModifiersLibrary.ClassA' could be found (are you missing a using directive or an assembly reference?)

结论,类成员变量的访问控制受限于其类的修饰符,如上面例子class为internal修饰符,则该类仅能在程序集内可被访问。

Protected Internal

对代码进行重构,在ClassA、ClassB、ClassC中添加如下代码:

  1: namespace AccessModifiersLibrary
  2: {
  3:     public class ClassA
  4:     {
  5:         protected internal void MethodClassA()
  6:         {
  7:
  8:         }
  9:     }
 10:
 11:     public class ClassB:ClassA
 12:     {
 13:         protected internal void MethodClassB()
 14:         {
 15:             MethodClassA();
 16:         }
 17:     }
 18:
 19:     public class ClassC
 20:     {
 21:         public void MethodClassC()
 22:         {
 23:             ClassA classA=new ClassA();
 24:             classA.MethodClassA();
 25:         }
 26:     }
 27: }
 28:
 29: using AccessModifiersLibrary;
 30:
 31:  namespace AccessModifiers
 32: {
 33:     public class Program
 34:     {
 35:         public static void Main(string[] args)
 36:         {
 37:             ClassC classC=new ClassC();
 38:             classC.MethodClassC();
 39:         }
 40:     }
 41: }

运行结果无错误。

结论:Protected internal 修饰符做了2件事情,protected约定类类和继承类访问控制,internal约定了只能在当前程序集中。

Protected 类成员变量

  1: namespace AccessModifiers
  2: {
  3:     class AAA
  4:     {
  5:         protected int a;
  6:         void MethodAAA(AAA aaa,BBB bbb)
  7:         {
  8:             aaa.a = 100;
  9:             bbb.a = 200;
 10:         }
 11:     }
 12:      class BBB:AAA
 13:      {
 14:          void MethodBBB(AAA aaa, BBB bbb)
 15:          {
 16:              aaa.a = 100;
 17:              bbb.a = 200;
 18:          }
 19:      }
 20:     public class Program
 21:     {
 22:         public static void Main(string[] args)
 23:         {
 24:         }
 25:     }
 26: }

编译结果:

Cannot access protected member 'AccessModifiers.AAA.a' via a qualifier of type 'AccessModifiers.AAA'; the qualifier must be of type 'AccessModifiers.BBB' (or derived from it)

结论:AAA中定义了一个a的protected变量,其仅能在自己内部访问和继承其的子类内访问。但是,通过传参方式传入的则无法访问--这里要求是public权限。

继承中访问优先级

看代码:

  1:  namespace AccessModifiers
  2: {
  3:     class AAA
  4:     {
  5:
  6:     }
  7:     public class BBB:AAA
  8:      {
  9:
 10:      }
 11:     public class Program
 12:     {
 13:         public static void Main(string[] args)
 14:         {
 15:         }
 16:     }
 17: }

编译报错:

Compile time error: Inconsistent accessibility: base class 'AccessModifiers.AAA' is less accessible than class 'AccessModifiers.BBB'

子类不能比其基类的访问控制符作用域范围大,如上面的例子中,基类为internal,而子类为public则报错了。

去掉继承,代码重构为如下结果:

  1:  namespace AccessModifiers
  2: {
  3:     class AAA
  4:     {
  5:
  6:     }
  7:     public class BBB
  8:      {
  9:         public AAA MethodB()
 10:         {
 11:             AAA aaa= new AAA();
 12:             return aaa;
 13:         }
 14:      }
 15:     public class Program
 16:     {
 17:         public static void Main(string[] args)
 18:         {
 19:         }
 20:     }
 21: }

编译结果:

Inconsistent accessibility: return type 'AccessModifiers.AAA' is less accessible than method 'AccessModifiers.BBB.MethodB()'

这样也编译不通过,因为AAA为internal的访问类型,在public BBB中返回了public的AAA,则意味着在其他程序集中也可能访问AAA,这样是违法了internal修饰符原则,故编译报错。

同理,如下的代码也是一样的问题导致编译报错:

  1:  namespace AccessModifiers
  2: {
  3:     class AAA
  4:     {
  5:
  6:     }
  7:     public class BBB
  8:     {
  9:         public AAA aaa;
 10:     }
 11:     public class Program
 12:     {
 13:         public static void Main(string[] args)
 14:         {
 15:         }
 16:     }
 17: }

如对代码做重构,去掉BBB中AAA变量的修饰,既默认为private访问修饰符,则编译没有错误了。

  1:  namespace AccessModifiers
  2: {
  3:     class AAA
  4:     {
  5:
  6:     }
  7:     public class BBB
  8:     {
  9:          AAA a;
 10:     }
 11:     public class Program
 12:     {
 13:         public static void Main(string[] args)
 14:         {
 15:         }
 16:     }
 17: }

参考MSDN中修饰符说明:

public

同一程序集中的任何其他代码或引用该程序集的其他程序集都可以访问该类型或成员。

private

只有同一类或结构中的代码可以访问该类型或成员。

protected

只有同一类或结构或者此类的派生类中的代码才可以访问的类型或成员。

internal

同一程序集中的任何代码都可以访问该类型或成员,但其他程序集中的代码不可以。

protected internal

由其声明的程序集或另一个程序集派生的类中任何代码都可访问的类型或成员。 从另一个程序集进行访问必须在类声明中发生,该类声明派生自其中声明受保护的内部元素的类,并且必须通过派生的类类型的实例发生。

同时,C#中类、枚举、结构体等修饰符规则表如下:

深入浅出OOP(五): C#访问修饰符(Public/Private/Protected/Internal/Sealed/Constants)

Sealed Classes

Sealed修饰符的类,不可被其他类继承。

  1:  namespace AccessModifiers
  2: {
  3:     sealed class AAA
  4:     {
  5:
  6:     }
  7:     class BBB:AAA
  8:     {
  9:
 10:     }
 11:     public class Program
 12:     {
 13:         public static void Main(string[] args)
 14:         {
 15:         }
 16:     }
 17: }

运行报错:

'AccessModifiers.BBB': cannot derive from sealed type 'AccessModifiers.AAA'

深入浅出OOP(五): C#访问修饰符(Public/Private/Protected/Internal/Sealed/Constants)

Sealed类使用如下:

  1: using System;
  2:
  3: namespace AccessModifiers
  4: {
  5:     sealed class AAA
  6:     {
  7:         public int x = 100;
  8:         public void MethodA()
  9:         {
 10:             Console.WriteLine("Method A in sealed class");
 11:         }
 12:     }
 13:     public class Program
 14:     {
 15:         public static void Main(string[] args)
 16:         {
 17:             AAA aaa=new AAA();
 18:             Console.WriteLine(aaa.x);
 19:             aaa.MethodA();
 20:             Console.ReadKey();
 21:         }
 22:     }
 23: }

运行正常。

Constants

  1: public class Program
  2:     {
  3:         private const int x = 100;
  4:         public static void Main(string[] args)
  5:         {
  6:             Console.WriteLine(x);
  7:             Console.ReadKey();
  8:         }
  9:     }

运行结果:

100

结论,Const变量在初始化的时候设定了初始值,可被使用,但不可修改值。同时const变量支持互相引用运算。

  1:  using System;
  2:
  3: namespace AccessModifiers
  4: {
  5:     public class Program
  6:     {
  7:         private const int x = y + 100;
  8:         private const int y = z - 10;
  9:         private const int z = 300;
 10:
 11:         public static void Main(string[] args)
 12:         {
 13:            System.Console.WriteLine("{0} {1} {2}",x,y,z);
 14:             Console.ReadKey();
 15:         }
 16:     }
 17: }

但是请不要循环依赖,否则编译器会检测报错:

  1: using System;
  2:
  3: namespace AccessModifiers
  4: {
  5:     public class Program
  6:     {
  7:         private const int x = y + 100;
  8:         private const int y = z - 10;
  9:         private const int z = x;
 10:
 11:         public static void Main(string[] args)
 12:         {
 13:            System.Console.WriteLine("{0} {1} {2}",x,y,z);
 14:             Console.ReadKey();
 15:         }
 16:     }
 17: }

检测报错:

The evaluation of the constant value for 'AccessModifiers.Program.x' involves a circular definition

本篇小结

  1. Class成员的默认修饰符为private
  2. class 被标记为internal仅能被当前程序集访问.
  3. Namespace默认为public修饰符,且不能添加修饰符。
  4. class可以使用public 或 internal修饰符.不能使用修饰符 protected private. class默认的修饰符为internal.
  5. 类成员可使用所有修饰符,默认为 private.
  6. Protected internal修饰符约定了仅在继承类内有效.
  7. 在public 与 internal修饰符之间,public通常有更大的访问权限.
  8. 基类必须必子类有更大的修饰符访问权限,才可被子类继承.
  9. 函数返回值的修饰符要有能访问返回值的权限.
  10. sealed Class无法被子类继承.
  11. const变量,需要在声明时完成初始化,在编码阶段不能初始化.
  12. 类的const变量,可以彼此引用,但是不能形成循环引用.
  13. const变量在编译器进行初始化,故const的运算可被执行.
  14. const变量不能被标记为static.
  15. Static 变量在类首次被加载时候初始化. int类型默认初始化为0,bool被初始化为False.
  16. static readonly 字段无法被赋值,static构造函数或者变量初始化时刻除外.

参考原文:Diving into OOP (Day 5): All About C# Access Modifiers (Public/Private/Protected/Internal/Sealed/Constants/Static and Readonly Fields)

文章目录:

上一篇:深入浅出OOP(六): 理解C#的Enums


下一篇:C++ //多态 //静态多态:函数重载 和 运算符重载 属于静态多态 ,复用函数名 //动态多态:派生类和虚函数实现运行时多态