第四章 簡介
方法的結構重複問題
我們在上一篇正式整理完畢,從這一篇開始,我們要再次進入學習收集示例階段了。
那麼我們學什麼呢?當然是學習設計工具,也就是在上篇中提到的關鍵知識點。這些關鍵知識點,大部分來自於 C# 語法。
不過在此之前,我們先實現一個功能,這個功能是,傳入幾個數字,隨機取出其中一個數字。
比如傳入 1,3,5,7 從這四個數字中隨機取出一個數字出來。
其實現很簡單 程式碼如下:
using UnityEngine;
namespace QFramework
{
public partial class MathUtil
{
public static int GetRandomValueFrom(int[] values)
{
return values[Random.Range(0, values.Length)];
}
}
}
傳入一個,int 陣列,從 int 型別陣列中隨機取一個數值進行返回。
用法也很簡單,使用程式碼如下:
var randomValue = GetRandomValueFrom(new int[] {5, 100, -1, -3, 5});
但是隨著時間發展,我們有時候會需要隨機獲取其他型別值,比如從一個 float 型別的陣列中隨機取回一個 float 值,或者是從 string 型別的陣列中取回一個值。
難道我們要每個型別都實現一次嘛? 如果都實現一次的話,會增多很多重複結構的程式碼,注意看這裡是重複的結構。
什麼叫重複的結構,以下程式碼就是三個重複結構的方法。
public static int GetRandomValueFrom(int[] values)
{
return values[Random.Range(0, values.Length)];
}
public static string GetRandomValueFrom(string[] values)
{
return values[Random.Range(0, values.Length)];
}
public static float GetRandomValueFrom(float[] values)
{
return values[Random.Range(0, values.Length)];
}
雖然對初學者來說,知道把方法做成工具類已經很不容易了,但是以上這樣的程式碼肯定是不行的。
所以就要學習新的知識,但是筆者接下來要說的這個知識大家應該很熟悉了。
就是面試中常問的:物件導向的特性?
答:封裝、繼承、多型。
而我們要用其中的繼承特性。
我們先複習一下繼承:B 繼承 A,那麼 B 物件就可以擁有 A 中的 public、protect 方法和成員變數。
複習完了。。。
繼承的本質是,什麼是什麼的問題,比如 B 繼承 A,那麼我們可以使用 A 變數去接收 B 物件。
程式碼如下:
class A
{
}
class B : A
{
}
A a = new B();
這樣的的程式碼是沒問題的。
雖然以上程式碼和多型性有一點關係,不過我們只要記住,父類的變數可以接收子類的例項這個結論就好了。
那麼對我們的問題有什麼幫助呢?
我麼再來看下問題程式碼:
public static int GetRandomValueFrom(int[] values)
{
return values[Random.Range(0, values.Length)];
}
public static string GetRandomValueFrom(string[] values)
{
return values[Random.Range(0, values.Length)];
}
public static float GetRandomValueFrom(float[] values)
{
return values[Random.Range(0, values.Length)];
}
int、string、float 如果有一個共同的父類,那麼就可以用一個共同的父類,作為引數的型別。
那麼 int、string、float 有沒有共同的父類?答案是有的,它們共同繼承了 object 類。不止是int、string、float,C# 中的所有型別都繼承了 object 類。
我們有了答案,可以進行更改了,改完的程式碼如下:
public static object GetRandomValueFrom(object[] values)
{
return values[Random.Range(0, values.Length)];
}
我們看下使用的程式碼
var intRandomValue = (int)GetRandomValueFrom(new int[]{1,2,3});
var stringRandomValue = (string)GetRandomValueFrom(new string[]{"asdasd","123123"});
var floatRandomValue = (float)GetRandomValueFrom(new float[]{ 0.1f,0.2f });
使用的程式碼雖然比較難看(1.強制型別轉換;2.再加上每次傳入引數都要構造陣列;)
使用上有一點麻煩,不過還好,我們最起碼解決了結構重複的問題,而且我們還複習了一下繼承。
當然除了使用 object 接收,還有更好的方法-使用泛型。之所以先介紹 object ,是因為,筆者在沒接觸泛型之前,都是這麼搞的,這樣搞其實還有個問題,就是值型別轉引用型別會造成效率問題,不過這些問題下一篇再解決,更重要的是,我們今天的問題得到了進展,這是很值得開心的。
泛型:結構複用利器
在上一篇我們使用 object 解決了方法結構重複的問題,而在文章的尾部又提了一下更好的方法,就是泛型。
泛型對很多初學者來說是比較高階的概念,這裡呢我們順便複習一下泛型。
泛型是什麼呢?對於方法來說,方法結構中的部分或全部型別都可以先不進行定義,而是到呼叫方法的時候再去定義。
我們的 GetRandomValue 的目前程式碼如下 :
public static object GetRandomValueFrom(object[] values)
{
return values[Random.Range(0, values.Length)];
}
測試程式碼如下:
var intRandomValue = (int)GetRandomValueFrom(new int[]{1,2,3});
var stringRandomValue = (string)GetRandomValueFrom(new string[]{"asdasd","123123"});
var floatRandomValue = (float)GetRandomValueFrom(new float[]{ 0.1f,0.2f });
將 object 改成泛型後如下:
public static T GetRandomValueFrom<T>(T[] values)
{
return values[Random.Range(0, values.Length)];
}
測試程式碼如下:
var intRandomValue = GetRandomValueFrom(new int[]{1,2,3});
var stringRandomValue = GetRandomValueFrom(new string[]{"asdasd","123123"});
var floatRandomValue = GetRandomValueFrom(new float[]{ 0.1f,0.2f });
從測試程式碼中可以比較出來,使用泛型之後的程式碼確實好用了很多。
大家思考下泛型是不是這樣的?結構中的部分或全部型別都可以先不進行定義,而是到呼叫的時候再去定義。
我們有收穫了一個法寶泛型。要說方法是邏輯上的複用,那麼泛型就是結構上的複用。兩大複用法寶。
關於泛型就先介紹到這裡,我們以後還會遇到使用泛型的時候的。
在看下當前的測試程式碼:
var intRandomValue = GetRandomValueFrom(new int[]{1,2,3});
var stringRandomValue = GetRandomValueFrom(new string[]{"asdasd","123123"});
var floatRandomValue = GetRandomValueFrom(new float[]{ 0.1f,0.2f });
目前比較麻煩的是陣列構造程式碼了。
這個也是有辦法搞定的。我們把方法的實現改成如下:
public static T GetRandomValueFrom<T>(params T[] values)
{
return values[Random.Range(0, values.Length)];
}
大家注意下,引數前邊加上了一個 params 關鍵字。這個是什麼意思呢?
意思是 GetRandomValueFrom 可以傳任意數量的引數。
我們先看下測試程式碼,測試程式碼呢其實可以改成如下:
var intRandomValue = GetRandomValueFrom(1, 2, 3);
var stringRandomValue = GetRandomValueFrom("asdasd", "123123");
var floatRandomValue = GetRandomValueFrom(0.1f, 0.2f);
是不是清爽了很多?這就是 params 的用法。
而通過 params 修飾的 values 引數來說,如果傳入的是一個陣列,那麼 values 本身就是這個陣列,如果傳入的是若干個值,那麼 values 中就包含了這若干個值。
總結一句話,就是可以傳一整個陣列,也可以傳若干個引數,設計得非常人性化。
到此呢,我們的第九個示例就已經非常完善了。
全部程式碼如下:
using UnityEngine;
namespace QFramework
{
public partial class MathUtil
{
#if UNITY_EDITOR
[UnityEditor.MenuItem("QFramework/9.從若干個值中隨機取出一個值", false, 10)]
#endif
private static void GetRandomValueFromMenuClicked()
{
Debug.Log(GetRandomValueFrom(1, 2, 3));
Debug.Log(GetRandomValueFrom("asdasd", "123123"));
Debug.Log(GetRandomValueFrom(0.1f, 0.2f));
}
public static T GetRandomValueFrom<T>(params T[] values)
{
return values[Random.Range(0, values.Length)];
}
}
}
程式碼執行結果如下:
編譯之後選單如下
目錄結構如下
我們進行一次匯出,這次匯出就正常匯出就好。
今天的內容就到這裡,我們下一篇再見,拜拜~。
轉載請註明地址:涼鞋的筆記:liangxiegame.com
更多內容
-
QFramework 地址:https://github.com/liangxiegame/QFramework
-
QQ 交流群:623597263
-
Unity 進階小班:
- 主要訓練內容:
- 框架搭建訓練(第一年)
- 跟著案例學 Shader(第一年)
- 副業的孵化(第二年、第三年)
- 權益、授課形式等具體詳情請檢視《小班產品手冊》:https://liangxiegame.com/master/intro
- 主要訓練內容:
-
關注公眾號:liangxiegame 獲取第一時間更新通知及更多的免費內容。