[C#] 類別成員存取關係一覧
本篇用圖表來介紹 C# 類別成員的存取關係,有 private
、protected
、public
,跟跨組件的成員存取,有 internal
、protected internal
、private protected
。以及比較一般繼承與多型繼承中,會取得什麼版本的方法。
類別成員的存取
存取來源 | private |
protected |
public |
---|---|---|---|
自己類別中 | |||
衍生類別中 | |||
外部 |
自己類別中
在自己類別中存取的情況是:
- 在類別定義中,存取類別的成員,如 Test1。
- 在類別定義中,透過該類別的物件存取成員,如 Test2。
所有 private
、protected
、public
的成員都可以存取。
public class Bar
{
private int x;
private void Func()
{
Console.WriteLine("Func called");
}
public void Test1()
{
x = 42;
Func();
}
public void Test2(Bar bar)
{
bar.x = 42;
bar.Func();
}
}
衍生類別中
在衍生類別中存取的情況是:
- 在衍生類別的定義中,存取基礎類別的成員,如 Test1。
- 在衍生類別的定義中,透過該類別物件存取基礎類別的成員,如 Test2。
protected
、public
的成員都可以存取,但 private
的成員不行。
public class Bar
{
protected int x;
protected void Func()
{
Console.WriteLine("Func called");
}
}
public class Foo : Bar
{
public void Test1()
{
x = 42;
Func();
}
public void Test2(Foo foo)
{
foo.x = 42;
foo.Func();
}
}
外部
在外部存取是指:在非衍生類別中,透過該類別的物件存取。
只有 public
成員才可以存取。
public class Bar
{
public int x;
public void Func()
{
Console.WriteLine("Func called");
}
}
public class SomeClass
{
public void Test()
{
var bar = new Bar();
bar.x = 42;
bar.Func();
}
}
跨組件的類別成員存取
在 C# 中,程式碼可以編譯到不同的組件(Assembly)中,並像模組或函式庫一樣,可以放到不同的程式中供取用。上述的 public
、protected
、privated
成員不受組件不同的影響,如 public
成員在也可以被不同組件的類別取用,但 C# 另外提供可以控制不同組件的類別成員存取的修飾詞。
相同組件中
存取來源 | internal |
protected internal |
private protected |
---|---|---|---|
自己類別中 | |||
衍生類別中 | |||
外部 |
不同組件中
存取來源 | internal |
protected internal |
private protected |
---|---|---|---|
衍生類別中 | |||
外部 |
-
internal
與public
相似,但只在相同組件中才是public
。 -
protected internal
比起internal
的權限稍為大一點,讓不同元件的衍生類別可以取用。 -
private protected
與protected
相似,但只在相同組件中才是protected
。
外部存取方法的版本
在基礎類別跟衍生類別中的 public
方法有相同簽章1時,會因為關鍵字(如:new
、virtual
)的不同而讓類別物件存取到不同版本的方法。
一般繼承
如果沒有同簽章的方法
public class Bar
{
public void Func()
{
Console.WriteLine("Bar.Func");
}
}
public class Foo : Bar
{
}
Bar b |
Foo f |
|
---|---|---|
new Bar() |
“Bar.Func” | |
new Foo() |
“Bar.Func” | “Bar.Func” |
上方表格以 Bar b
和 new Bar()
為例,代表的是:
Bar b = new Bar();
b.Func();
欄位內則是會輸出的訊息。
如果有同簽章的方法
如果衍生類別中有跟基礎類別同簽章的方法,則會「隱藏」基礎類別的方法,編譯器會發出警告,加上 new
關鍵字則可以告訴編譯器就是要這樣作。
public class Bar
{
public void Func()
{
Console.WriteLine("Bar.Func");
}
}
public class Foo : Bar
{
public new void Func()
{
Console.WriteLine("Foo.Func");
}
}
Bar b |
Foo f |
|
---|---|---|
new Bar() |
“Bar.Func” | |
new Foo() |
“Bar.Func” (1.) | “Foo.Func” (2.) |
- 在一般繼承中,如果將衍生類別的物件賦值給基礎類別的變數,該物件會被隱含轉換(implict type conversion)成基礎類別的物件,所以會使用到基礎類別的版本。
- 因為衍生類別中「隱藏」了基礎類別的方法,所以衍生類別的物件會使用衍生類別的版本。
多型繼承
如果基礎類別有 virtual
方法,而且在衍生類別中用 override
「覆寫」該方法的話,即使把衍生類別的物件賦值給基礎類別的變數,也可以取用到衍生類別的版本,稱為多型繼承。在多型繼承下,就可以讓基礎類別的變數因給與不同的衍生類別的物件,呼叫同個方法而有衍生類別上的行為,不需使用多個衍生類別的物件。
如果沒有同簽章的方法
public class Bar
{
public virtual void Func()
{
Console.WriteLine("Bar.Func");
}
}
public class Foo : Bar
{
}
Bar b |
Foo f |
|
---|---|---|
new Bar() |
“Bar.Func” | |
new Foo() |
“Bar.Func” | “Bar.Func” |
如果基礎類別的方法沒有被「覆寫」的話,就跟一般繼承一樣,都是基礎類別的版本。
如果有同簽章的方法
public class Bar
{
public virtual void Func()
{
Console.WriteLine("Bar.Func");
}
}
public class Foo : Bar
{
public override void Func()
{
Console.WriteLine("Foo.Func");
}
}
Bar b |
Foo f |
|
---|---|---|
new Bar() |
“Bar.Func” | |
new Foo() |
“Foo.Func” (1.) | “Foo.Func” |
- 把衍生類別的物件賦值給基礎類別的變數,雖然會被隱含轉換,但是在多型繼承下,基礎類別的方法會被「覆寫」,所以會取用到衍生類別的版本。
參考資料
- 存取修飾詞 - Microsoft Docs
- 方法簽章 - Microsoft Docs
- 多型繼承 - Microsoft Docs
- 了解使用 Override 和 New 關鍵字的時機 - Microsoft Docs
-
方法簽章(method signature)由存取層級(access level,如:
public
)、選擇性修飾詞(optional modifier,如:abstract
)、回傳值、方法名稱、方法參數(parameters)構成。 ↩