Unity 遊戲框架搭建 2019 (三十九、四十一) 第四章 簡介&方法的結構重複問題&泛型:結構複用利器

涼鞋的筆記發表於2020-04-30

第四章 簡介

006tNc79gy1fzfsej80nwj30gb0mcacz.jpg

方法的結構重複問題

我們在上一篇正式整理完畢,從這一篇開始,我們要再次進入學習收集示例階段了。

那麼我們學什麼呢?當然是學習設計工具,也就是在上篇中提到的關鍵知識點。這些關鍵知識點,大部分來自於 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)];
		}
	}
}

程式碼執行結果如下:
006tNc79gy1fzfsfiotsuj30e609iwf7.jpg

編譯之後選單如下006tNc79gy1fzfsfm17ahj30ia0cmdmz.jpg

目錄結構如下
006tNc79gy1fzfsfo9rylj30jm0ccwg7.jpg

我們進行一次匯出,這次匯出就正常匯出就好。

今天的內容就到這裡,我們下一篇再見,拜拜~。

轉載請註明地址:涼鞋的筆記:liangxiegame.com

更多內容

相關文章