深入浅出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#中類、枚舉、結構體等修飾符規則表如下:
?
?
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'
?
?
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
?
本篇小結
?
參考原文:Diving into OOP (Day 5): All About C# Access Modifiers (Public/Private/Protected/Internal/Sealed/Constants/Static and Readonly Fields)
?
文章目錄:
- 深入淺出OOP(一): 多態和繼承(早期綁定/編譯時多態)
- 深入淺出OOP(二): 多態和繼承(繼承)
- 深入淺出OOP(三): 多態和繼承(動態綁定/運行時多態)
- 深入淺出OOP(四): 多態和繼承(抽象類)
?
本文是由葡萄城技術開發團隊發布,轉載請注明出處:葡萄城官網
from:?https://www.cnblogs.com/powertoolsteam/p/Diving-into-OOP-Day-All-About-Access-Modifiers-in.html
總結
以上是生活随笔為你收集整理的深入浅出OOP(五): C#访问修饰符(Public/Private/Protected/Internal/Sealed/Constants)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: C# 索引器(Indexer)
- 下一篇: C#类中的internal成员可能是一种