前言:
首先,小匹夫要祝各位看官聖誕快樂,新年愉快~。上一篇文章《自己動手,實現一種類似List<T>的數據結構(一)》 介紹了一下不依靠List<T>實現的各種接口,仿造一個輕量級數據結構的過程。可能有的看官會有一些疑問,例如一些功能可以通過Linq提供的拓展來實現呀。此言不虛但也不全對,為了我們在工作中能方便的操作集合而提供的這些拓展方法(包括我們自己也可以構建的拓展方法),例如 Where,Any,Max,All...balalbala等等這些方法都是針對IEnumerable的對象進行擴展的,也就是說需要實現 IEnumerable接口。但是前面已經說了,小匹夫的用意是不實現各種List<T>繼承的接口。另外小匹夫的初衷是仿造和拓展 List<T>,將工作中需要使用到的各種功能集成到一個類中,所以有些現成的拓展方法不需要,有一些沒有的方法小匹夫也會自己實現一下(當然不是通過給現成的類添加拓展方法這種方式)。當然這篇文章介紹的東西還不成熟,需要慢慢完善,小匹夫也是把這個當做一個學習和實踐的機會。好啦,解釋完畢,那就介紹下今天的內容吧:
- 實現的方法的名稱和說明列表
- 增加了3個委托來抽象3種情況。
- Map:通過委托把EggArray<T>中的每個值映射到一個新的EggArray<T>中。
- Difference:返回的值來自EggArray<T>中,但同時不是傳入的Other里面的值
- Invoke:在EggArray<T>的每個元素上執行methodName方法。
- Pluck:萃取EggArray<T>中某字段值,返回一個數組,由於字段類型不確定,所以需要裝箱。
- Shuffle:返回一個隨機亂序的T[]副本。
那么下面我們就書接上文,繼續我們仿照和拓展List<T>的步伐。
Underscore.js的前緣
咦,這不是一篇關於Csharp的文章嗎?怎么把JS給干出來?哈哈,當然技術上並沒有什么必然的關系,只不過是小匹夫之前使用過cocos2d這套游戲引擎開發過游戲,有一段時間也很痴迷於cocos2d-js這種使用JS就能開發原生游戲的能力。所以也接觸了一些js庫,對Underscore.js更是情有獨鍾。所以一提到要模仿List<T>這種內部其實是Array的數據結構,一個靈感就是為何不嘗試實現一些Underscore.js數組部分的若干功能呢?所以下表EggArray<T>的新增方法中有部分借鑒於Underscore.js。
新增方法表
新增方法 | 說明 |
First | 返回EggArray<T> 的第一個元素。傳遞 n參數將返回數組中從第一個元素開始的n個元素 |
Last | 返回EggArray<T> 的最后一個元素。傳遞 n參數將返回數組中從最后一個元素開始的n個元素 |
Slice | 切割 |
Get | 預留 |
Set | 預留 |
AddFirst | 將對象添加到 EggArray<T> 的起始處。 |
RemoveLast | 從 EggArray<T> 中移除特定對象的最后一個匹配項。 |
ContainsStrict | 確定某元素是否在 EggArray<T> 中。(嚴格判斷是否是同一個對象) |
IndexOfStrict | 搜索指定的對象,並返回整個 EggArray<T> 中第一個匹配項的從零開始的索引。(同上) |
TryGet | 獲取指定類型對象 |
LastIndexOf | 搜索指定的對象,並返回整個 EggArray<T> 中第一個匹配項的從結尾開始的索引。 |
Map | 通過委托把EggArray<T>中的每個值映射到一個新的EggArray<T>中 |
Filter | 遍歷EggArray<T>中的每個值,返回包含所有通過predicate真值檢測的元素值。 |
Without | 返回一個刪除所有values值后的 EggArray<T>副本。 |
Find | 在EggArray<T>中逐項查找,返回第一個通過predicate迭代函數真值檢測的元素值 |
Every | 如果EggArray<T>中的所有元素都通過predicate的真值檢測就返回true。 |
Some | 如果EggArray<T>中有任何一個元素通過 predicate 的真值檢測就返回true。 |
Partition | 拆分一個EggArray<T>為兩個數組: 第一個數組其元素都滿足predicate迭代函數, 而第二個的所有元素均不能滿足predicate迭代函數 |
Difference | 返回的值來自EggArray<T>中,但同時不是傳入的Other里面的值 |
Uniq | 返回 EggArray<T>去重后的副本 |
Invoke | 在EggArray<T>的每個元素上執行methodName方法。 |
Pluck | 萃取EggArray<T>中元素某屬性值,返回一個數組。 |
Shuffle | 返回一個隨機亂序的T[]副本 |
Sample
|
從 EggArray<T>中產生一個隨機樣本。傳遞一個數字表示從EggArray<T>中返回n個隨機元素。否則將返回一個單一的隨機項。 |
各位看官可以看到,增加了許多挺有趣的功能。為了能將表中的功能名字變成真正的功能,我們還需要對上一篇文章中的變量&屬性部分做一些增改,如下我們增加了3個委托來抽象3種情況。
//定義三個委托來處理具體邏輯 public delegate void IterationHandler(T item); public delegate bool IterationBoolHandler(T item); public delegate T IterationVauleHandler(T item);
同時為了能測試我們的功能,我們還要定義一個用來被當做元素測試的類。
//被測試類 public class TargetClass { public int id; public string name; public TargetClass(int id) { this.id = id; this.name = "NO. " + id; } public void Hi() { Debug.Log ("say hi"); } }
同時還要有一個測試的環境,因為小匹夫是用mac做unity3d的開發,所以就直接使用unity3d的環境了。
/// <summary> /// Egg array test.Based on Unity3D,各個元素的id為0-9 /// </summary> using UnityEngine; using System.Collections; using EggToolkit; public class EggArrayTest : MonoBehaviour { EggArray<TargetClass> testArray = new EggArray<TargetClass>(); // Use this for initialization void Start () { for(int i = 0; i < 10; i++) { TargetClass test = new TargetClass(i); testArray.Add(test); } // Test_Difference(); // Test_Invoke(); // Test_Pluck(); // Test_Shuffle(); // Test_Map(); } void Update () { } }
下面就讓小匹夫帶領大家分析幾個具體的函數,並進行下測試吧。
Map:
使用了IterationVauleHandler這個委托,即需要返回一個T類型的值。
//通過委托把EggArray<T>中的每個值映射到一個新的EggArray<T>中 public EggArray<T> Map(EggArray<T>.IterationVauleHandler handler) { EggArray<T> targetArray = new EggArray<T>(this.capacity); for(int i = 0; i < this.count; i++) { T t = handler(this.items[i]); targetArray.Add(t); } return targetArray; }
在EggArrayTest中實現Test_Map這個方法:
void Test_Map() { EggArray<TargetClass> newArray = testArray.Map(delegate(TargetClass item) { TargetClass newItem = new TargetClass(1); newItem.id = item.id * 10; return newItem; }); newArray.Foreach(delegate(TargetClass item) { Debug.Log (item.id); }); }
//原元素的id為0-9,輸出為0,10,20...90
Difference:
調用了Filter方法,其中Filter方法的參數是一個IterationBoolHandler委托,即一個返回bool值的委托。具體可以看Filter的實現。
/// <summary> /// Difference the specified others. ///輸出不包含others中元素的EggArray<T> /// </summary> /// <param name="others">Others.</param> public EggArray<T> Difference(EggArray<T> others) { EggArray<T> targetArray = new EggArray<T>(); targetArray = this.Filter(delegate(T item) { bool b = !others.Contains(item); return b; }); return targetArray; }
在EggArrayTest中實現Test_Difference這個方法:
//作為參數傳入的EggArray<T>由testArray的第5,第9這2個元素組成 void Test_Difference() { EggArray<TargetClass> differentArray = new EggArray<TargetClass>(); differentArray.Add(testArray.Get(5)); differentArray.Add(testArray.Get(9)); testArray.Difference(differentArray).Foreach(delegate(TargetClass item) { Debug.Log(item.name); }); } //輸出缺少no. 5,no. 9這兩個name
Invoke:
在EggArray<T>的每個元素上執行methodName方法。
/// <summary> /// Invoke the specified methodName. /// 每個元素上執行methodName方法,若方法不存在則拋出exception /// </summary> /// <param name="methodName">Method name.</param> public void Invoke(string methodName) { Type t = typeof(T); var method = t.GetMethod(methodName); if(method == null) throw new Exception("沒有找到指定的方法哦~,可能不叫" + methodName); for(int i = 0; i < this.count; i++) { method.Invoke(this.items[i], null); } }
在EggArrayTest中實現Test_Invoke這個方法:
//調用TargetClass的HI()方法 void Test_Invoke() { testArray.Invoke("Hi"); } //輸出:say hi
Pluck:
萃取EggArray<T>中某字段值,返回一個數組,由於字段類型不確定,所以需要裝箱。當傳入的名稱無法查找到該字段時,拋出exception。
/// <summary> /// Pluck the specified fieldName. /// 萃取某字段值,返回一個數組 /// 由於字段類型不確定,所以需要裝箱 /// </summary> /// <param name="fieldName">Field name.</param> public object[] Pluck(string fieldName) { Type t = typeof(T); object[] targetArray = new object[this.count]; var field = t.GetField(fieldName); if(field == null) throw new Exception("沒有找到指定的field哦~,可能不叫" + fieldName); for(int i = 0; i < this.count; i++) { object value = field.GetValue(this.items[i]); targetArray[i] = value; } return targetArray; }
在EggArrayTest中實現Test_Pluck這個方法:
//獲取各個元素 字段id的值 void Test_Pluck() { object[] testObj = testArray.Pluck("id"); string testString = string.Empty; for(int i = 0; i < testObj.Length; i++) { testString = testObj[i].ToString(); Debug.Log ("field value is " + testString); } } //輸出為0-9
Shuffle:
返回一個隨機亂序的T[],下面看代碼
/// <summary> /// Shuffle this instance. /// 返回一個隨機亂序的副本 /// </summary> public T[] Shuffle() { T[] shuffled = new T[this.count]; Random random = new Random(); for (int index = 0, rand; index < this.count; index++) { rand = random.Next(index); if (rand != index) shuffled[index] = shuffled[rand]; shuffled[rand] = this.items[index]; } return shuffled; }
在EggArrayTest中實現Test_Shuffle這個方法:
// void Test_Shuffle() { TargetClass[] test = testArray.Shuffle(); for(int i = 0; i < test.Length; i++) { Debug.Log (test[i].name); } } //默認順序為NO. 0 ~ NO. 9 //亂序后,見圖
好了,這周就到這里~小匹夫最近也在趕項目的途中,所以測試和修改的精力也被消耗了很多。過完元旦之后,再繼續~
末了還是要說一聲:各位元旦快樂~
完整的代碼和測試可以在這里獲取:https://github.com/chenjd/Unity3D_EggArray