存取修飾詞 Access Modifier

C# 的存取修飾詞有以下六種

public
所有人都可以存取
protected
只有所在類別以及他的的衍生類別可以存取
private
只有所在類別可以存取
internal
只有在相同 assembly 的類別可以存取
protected internal
只有在相同 assembly 的類別或是所在類別以及他的的衍生類別可以存取 (也就是 protected OR internal)
private protected (C# 7.2 新增)
只有在相同 assembly 的類別以及所在類別以及他的的衍生類別可以存取 (也就是 protected AND internal)

從這張簡圖就很容易了解不同修飾詞的範圍 修飾詞的範圍

另外 Type 只能宣告為 publicinternal,Type Member 可以用所有的存取修飾詞宣告。

Default Access Modifier

預設值總是可見度最低的存取修飾詞。Type 為 internal,Type Member 為 private

預設的存取修飾詞 可用的存取修飾詞
namespace level
enum internal public, internal
interface internal public, internal
class internal public, internal
struct internal public, internal
delegate internal public, internal
type level
enum private public, protected, internal, private, protected internal, private protected
interface private public, protected, internal, private, protected internal, private protected
class private public, protected, internal, private, protected internal, private protected
struct private public, protected, internal, private, protected internal, private protected
delegate private public, protected, internal, private, protected internal, private protected
enum value public public
interface member public public
class member private public, protected, internal, private, protected internal, private protected
struct member private public, internal, private

以軟體設計的原則,應該讓類別、變數的可見性最小化。也就是說不需要公開的類別就不應該設為 public

如果是開發共用的函式庫,我們希望讓程式碼被其他專案重複使用時,我們通常會把共用的函式庫放在獨立的資料夾與命名空間內。

另外也可以用獨立的儲存庫來存放,並且用 submodule 的方式加入到我們的專案中。使得我們可以更好地跨專案共用資料。

Unity Special folders

在 Unity 中,除了位於特殊資料夾以外的所有檔案會被放在遊戲專案中,為了降低程式間的耦合,以及避免在開發階段中意外將遊戲的功能寫到共用的函式庫的問題,我們可以利用一些 Unity 的機制來達成。

根據 Unity 的說明,依據檔案所在的資料夾不同,script 會被分配到不同的專案中。也會有不同的編譯順序。根據官方說明,通常可以分成四個專案編譯:

  • Phase 1: 在 Standard Assets、Pro Standard Assets 以及 Plugins 中非 Editor 資料夾的程式碼
  • Phase 2: 在 Standard Assets、Pro Standard Assets 以及 Plugins 中 Editor 資料夾的程式碼
  • Phase 3: 在 Standard Assets、Pro Standard Assets 以及 Plugins 以外非 Editor 資料夾的程式碼
  • Phase 4: 在 Standard Assets、Pro Standard Assets 以及 Plugins 以外的 Editor 資料夾的程式碼

Ref. Special folders and script compilation order

如果你使用 Visual Studio for Mac 開啟 Solution 只會看到一個專案。你可以在 Solution 上按右鍵 -> 顯示選項 -> 顯示 Unity 專案總管,取消勾選後就會看到實際的專案分佈。

實際上檢查各專案的參考設定可以得到底下的相依圖。

專案相依圖

了解了這些細節,我們可以這樣規劃我們的專案:

遊戲專案用到的第三方專案(Ex. Asset Store 買來的插件)

當第三方 Plugin 放在 Assets 資料夾內,而不是 Plugins 資料夾內,就會讓遊戲專案可以存取到 Plugin 的 internal 成員,這通常會破壞了 Plugin 想要對外隱藏的資訊。 我們可以將 Plugin 移動到 Plugins 資料夾內來維持 Plugin 的封裝性。

部分 Plugin 可能沒有考慮到會被移動到 Plugins 內,所需要實際測試看看。

需要注意的是:移動資料夾可能會造成之後更新 Plugin 時候的維護成本增加,也需要在搬移時考慮是否有必要。通常不會有問題,畢竟我們比較少會修改 Plugin 的內容。

  • 在 Unity 2017.3 之後可以用 Assembly definition files 來解決 (稍後提到)。
  • 在 Unity 2017.2 以前可以先保留在 Plugin 原始的位置,使用上注意不要誤用到 internal 的成員。

對於跨專案共用的函式庫

  • 在 Unity 2017.2 以前可以將共用的函式庫放在 Plugins 之中,並且用 submodule 放到獨立的儲存庫。
  • 如果將共用的函式庫放在 Plugins 以外,如此一來共用函式庫的內容就會跟遊戲專案在同一個 Assembly 之中。如果在新增或修改程式時沒有注意到,很容易在共用函式庫中呼叫遊戲專案的程式,造成其他用到此共用函式庫的專案編譯失敗。放在 Plugins 可以讓這個情形在編譯時就發生錯誤,避免在其他專案更新時才發現錯誤。
  • 在 Unity 2017.3 之後可以用 Assembly definition files 來指定 Plugins 以外的資料夾被產生成獨立的 Assembly 來避免不必要的耦合,我們可以用 submodule 將資料夾放到獨立的儲存庫。

Assembly definition files

從 Unity 2017.3 開始,我們可以針對不同的資料夾指定要編譯到獨立的專案中,並且設定彼此之間的相依關係。如此一來,就可以把 Plugin 放在各自的 Assembly 之中,避免彼此影響,也不需要改變現有資料夾架構,只需要加入一個定義檔即可。

使用時只要在資料夾上按右鍵,選擇  Assets > Create > Assembly Definition 就會新增一個定義檔。然後在 Inspector 上將名稱與相依的 Assembly 設定好,在這個資料夾內的 Script 就會被分配到你指定名稱的專案中。你可以開啟 Visual Studio for Mac 確認。

Editor 不會自動建立到 Xxx.Editor 專案中,所以需要同時加上 Editor 的 Assembly Definition File。

Ref. Script compilation and assembly definition files

另外使用 Assembly Definition File 額外帶來的好處還有。各別專案變小之後,當專案內的檔案有變動時,不需要重新編譯整個 Assembly-CSharp 。所以也可以稍微提升編譯的速度。