1. 程式人生 > >Unity3D-C#擴充套件方法

Unity3D-C#擴充套件方法

什麼是擴充套件方法?

  擴充套件方法從字面上理解是指擴充套件的方法,而對應到面向物件程式設計這個格局中則是指為一個類提供的擴充套件方法。按照我們通常的理解,我們首先需要獲得某個類的原始碼,然後在這個類程式碼中增加成員方法,這樣就可以達到為一個類提供擴充套件方法的目的。可是不幸地是,這種方法在沒有原始碼的情況下就無法奏效了,而且我們人為地去改變原始碼有可能會破壞整個程式碼的穩定性。那麼有沒有一種方法能在不改變原始碼的前提下為某個類提供擴充套件方法呢?這就是我們今天要說的擴充套件方法,所以我們可以將擴充套件方法理解為在不改變原始碼的前提下向外部提供擴充套件方法的一種方式。C#中的擴充套件方法實現起來是相對來說比較簡單的,例如我們做在Unity3D遊戲開發的時候,可能會用到DOTween這個外掛。這個外掛是iTween的作者重新編寫一個動畫外掛,效率上比iTween有較大的提升。更為重要的一點是,它採用擴充套件方法這種實現方式,使得我們在呼叫這些API介面的時候難以感覺到我們是在使用一個外掛,更像是在使用Unity3D的原生函式,所以當我們使用DOTween + uGUI 這樣的組合的時候,內心會感到無比的舒暢,一切都像是水到渠成一般。

擴充套件方法有哪些特點?

  擴充套件方法在實現上和普通的面向物件程式設計是一樣的,換句話說,我們只需要定義一個類,然後在裡面新增並實現相應的方法即可。但是這裡需要注意的地方有三點,第一,實現擴充套件方法的類必須是靜態類且類的名稱和實現擴充套件方法的類無關;第二、實現擴充套件方法的類方法必須是靜態方法;第三、實現擴充套件方法的類方法的第一個引數必須是使用this關鍵字指明要實現擴充套件方法的類。例如,我們知道將一個合法字串型別轉換為整型,可以使用int.parse()方法,假如我們希望為string型別擴充套件一個ToInt方法應該怎麼辦呢?我們一起來看下面的這段程式碼:

1
2
3
4
5
6
7
8

9
10
11
12
13
14
15
16
/// <summary>
/// 1、定義一個靜態類
/// 2、靜態類的名稱和要實現擴充套件方法的具體類無關
/// </summary>
public static class SomeClass
{
/// <summary>
/// 3、實現一個具體的靜態方法
/// </summary>
/// <param name="str">4、第一個引數必須使用this關鍵字指定要使用擴充套件方法的型別</param>
/// <returns></returns>
public static int
ToInt(this string str)

{

return int.Parse(str);
}
}

需要注意的是C#支援擴充套件方法是從.NET3.5版本開始,所以在編寫擴充套件方法的時候請確保你的.NET版本是否滿足這一要求。提到版本問題,有很多朋友尤其是從Unity5.0以後開始學習Unity3D的朋友,常常會在我的部落格中留言提到我的程式碼無法在新環境下執行等等類似地問題,我覺得這個世界上更新速度最快的當屬IT技術了,大家使用新版本沒有問題,可是有時候因為技術發展中的歷史遺留問題例如Python2.7和Python3、Unity4.X和Unity5.X,這個時候可能出現版本不相容的問題,這個時候如果網路上的資源沒有及時更新,建議大家還是及時檢視官方的最新文件,因為在博主看來網路上的書籍或者相關文章都是用來參考的,古話說:盡信書不如無書,只有客觀、冷靜地判斷知識的正確與否,我們方能學到真正有用的知識。

  好了,現在我們編寫完這個擴充套件方法以後,就可以像下面這樣使用擴充套件方法了:

1
2
string str = "1234";
int val = str.ToInt();

這個示例向大家展示瞭如何編寫一個無引數的擴充套件方法,那麼當我們需要在擴充套件方法中傳入引數的時候該怎麼做呢?我們只需要在第一個引數後繼續加入引數的宣告就好了。例如我們在Unity3D中常常需要給一個3D物體設定座標,通常我們可以通過下面的程式碼來實現:

1
transform.position = new Vector3(1,1,1);

這個程式碼到目前為止是比較簡潔的,可是我們知道在Unity3D中除了position屬性以外還有localPosition屬性,如果我們的程式碼中再涉及座標計算的話,我相信這個程式碼一定會變得非常的長。更有甚者,有時候我們只想改變三維座標中的一個維度,可是我們必須給transform.position一個三維座標,毫無意外地此時的程式碼會變得更長。為了解決這個問題,我們可以擴展出三個方法SetPositionX、SetPositionY、SetPositionZ來分別為x、y、z三個座標分量進行賦值,我們繼續在SomeClass這個類中新增方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
/// <summary>
/// 設定Tranform的X座標
/// </summary>
/// <param name="tran">當前Transform</param>
/// <param name="x">X座標</param>
public static void SetPositionX(this Transform tran, float x)
{

tran.position = new Vector3(x, tran.position.y, tran.position.z);
}

/// <summary>
/// 設定Tranform的Y座標
/// </summary>
/// <param name="tran">當前Transform</param>
/// <param name="x">Y座標</param>
public static void SetPositionY(this Transform tran, float y)
{

tran.position = new Vector3(tran.position.x, y, tran.position.z);
}

/// <summary>
/// 設定Tranform的Z座標
/// </summary>
/// <param name="tran">當前Transform</param>
/// <param name="x">Z座標</param>
public static void SetPositionZ(this Transform tran, float z)
{

tran.position = new Vector3(tran.position.x, tran.position.y, z);
}

同樣的,我們現在可以直接為一個三維物體的座標進行賦值:

1
2
3
transform.SetPositionX(1.0f);
transform.SetPositionY(1.0f);
transform.SetPositionZ(1.0f);

使用擴充套件方法的利弊

  擴充套件方法使用起來得心應手,所以我們這裡來討論下使用擴充套件方法的利弊。好處當然是自由而任性地使用擴充套件方法對類進行擴充套件,而且擴充套件方法在Visual Studio中的智慧提示會以藍色向下箭頭進行標識。擴充套件方法的壞處則是要看設計擴充套件方法的人能否較好的駕馭這個特性啦,其實所有的技術都是一樣的,我常常在遊戲群裡聽到人鄙視Unity3D引擎,以UnReal Engine4為遊戲引擎世界裡的泰山北斗,我承認UE4的畫面效果好,可是能真正用好這個引擎的人有多少呢?擴充套件方法在使用的時候應該遵守就近原則,即是在最小的範圍內使用擴充套件方法,對具體類而非抽象類實現擴充套件方法。我們使用擴充套件方法無非是因為它在邏輯層需要這樣的功能,所以我們沒有必要去改變抽象層的邏輯,因為這樣會“汙染”整個程式碼。舉一個簡單的例子,我們知道.NET中的基類是object,如果我們對這個類進行擴充套件,毫無疑問它會影響所有繼承自object的類,這樣就會造成“汙染”,顯然是不可取的。

小結

  • 在C#中實現擴充套件方法的類必須是靜態類且類的名稱和實現擴充套件方法的類無關
  • 實現擴充套件方法的類方法必須是靜態方法
  • 實現擴充套件方法的類方法的第一個引數必須是使用this關鍵字指明要實現擴充套件方法的類
  • 實現擴充套件方法應遵守就近原則,在最小的範圍內使用擴充套件方法以避免造成“汙染”