Unity的C#編程教程_62_語言集成查詢 LINQ 詳解及應用練習

目錄:

LINQ: Querys

1. Any

  • LINQ: Language Integrated Query 語言集成查詢

  • 類似 MySQL,允許過濾數據(array 和 list)

  • 任務說明:

    • 建立一個名字的數組
    • 查詢某個名字是否在該數組中

之前的做法:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class LinqTest : MonoBehaviour
{
    public string[] names = { "xiaoA", "xiaoB", "xiaoC", "xiaoD" };

    // Start is called before the first frame update
    void Start()
    {
        foreach(var name in names)
        {
            if(name == "xiaoC")
            {
                Debug.Log("Name found!");
            }
        }
    }

    // Update is called once per frame
    void Update()
    {
        
    }
}

改用 LINQ:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System.Linq; // LINQ 需要使用這個庫

public class LinqTest : MonoBehaviour
{
    public string[] names = { "xiaoA", "xiaoB", "xiaoC", "xiaoD" };

    // Start is called before the first frame update
    void Start()
    {
        /*
        foreach(var name in names)
        {
            if(name == "xiaoC")
            {
                Debug.Log("Name found!");
            }
        }
        */

        var answer = names.Any(name => name == "xiaoE");
        // 在 names 數組中,是否有任何滿足條件的元素?
        // 條件是 name == "xiaoC"
        // 返回的是 bool 值
        Debug.Log("Fond xiaoE? Answer: " + answer);
    }

    // Update is called once per frame
    void Update()
    {
        
    }
}

2. Contains

  • 用於判斷是否存在具體的元素
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System.Linq; // LINQ 需要使用這個庫

public class LinqTest : MonoBehaviour
{
    public string[] names = { "xiaoA", "xiaoB", "xiaoC", "xiaoD" };

    // Start is called before the first frame update
    void Start()
    {
        /*
        foreach(var name in names)
        {
            if(name == "xiaoC")
            {
                Debug.Log("Name found!");
            }
        }
        */

        /*
        var answer = names.Any(name => name == "xiaoE");
        // 在 names 數組中,是否有任何元素可以滿足特定條件?
        // 條件是 name == "xiaoC"
        // 返回的是 bool 值
        */

        var answer = names.Contains("xiaoE");

        Debug.Log("Fond xiaoE? Answer: " + answer);
    }

    // Update is called once per frame
    void Update()
    {
        
    }
}

3. Distinct

  • 去除重複元素
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System.Linq; // LINQ 需要使用這個庫

public class LinqTest : MonoBehaviour
{
    public string[] names = new string[] { "xiaoA", "xiaoA", "xiaoB", "xiaoC", "xiaoD" };

    // Start is called before the first frame update
    void Start()
    {
        Debug.Log("Before");
        foreach (string name in names)
        {
            Debug.Log(name);
        }


        var newNames = names.Distinct();
        // Distinct 不會直接作用於原數組,所以需要賦值到一個新的變數

        Debug.Log("New");
        foreach (var name in newNames)
        {
            Debug.Log(name);
        }

        Debug.Log("After");
        foreach (var name in names)
        {
            Debug.Log(name);
        }
        
        
    }

    // Update is called once per frame
    void Update()
    {
        
    }
}

注意,Distinct 不會直接修改原數組。

4. Where

  • 為序列按照指定條件篩選
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System.Linq; // LINQ 需要使用這個庫

public class LinqTest : MonoBehaviour
{
    public string[] names = new string[] { "xiaoAA", "xiaoA", "xiaoBB", "xiaoCCCC", "xiaoDDDD" };

    // Start is called before the first frame update
    void Start()
    {
        var newNames = names.Where(name => name.Length > 6);
        // 對於原來數組中的元素進行篩選
        // 篩選條件是:長度大於 6

        foreach(var name in newNames)
        {
            Debug.Log(name);
        }
        
    }

    // Update is called once per frame
    void Update()
    {
        
    }
}

Challenge: Hands on with LINQ

  • 任務說明:
    • 建立一個數組,包含的都是整型元素,每個元素取值範圍 0~100
    • 篩選出 50 以上的數據
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System.Linq; // LINQ 需要使用這個庫

public class LinqTest : MonoBehaviour
{
    public int[] nums = { 10, 20, 35, 66, 78, 98, 45, 23, 67 };

    // Start is called before the first frame update
    void Start()
    {
        var newNums = nums.Where(num => num > 50);

        foreach(var num in newNums)
        {
            Debug.Log(num);
        }
    }

    // Update is called once per frame
    void Update()
    {
        
    }
}

LINQ: Order by Descending

  • 篩選後進行排序
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System.Linq; // LINQ 需要使用這個庫

public class LinqTest : MonoBehaviour
{
    public int[] nums = { 10, 20, 35, 66, 78, 98, 45, 23, 67 };

    // Start is called before the first frame update
    void Start()
    {
        var newNums = nums.Where(num => num > 50).OrderByDescending(n => n);
        // 篩選出大於 50 的元素,然後直接按照降序排列
        // 排序條件可以訂製,比如設定 n => -n,其實就按照升序排列了

        foreach (var num in newNums)
        {
            Debug.Log(num);
        }
    }

    // Update is called once per frame
    void Update()
    {
        
    }
}

Challenge: Filter Items with LINQ

  • 任務說明:
    • 創建一個 class,用作篩選
    • 篩選對象為遊戲道具
    • 查詢 id 為 3 的物品是否存在,列印查詢結果
    • 篩選 50 元以上的物品,列印查詢結果
    • 計算所有道具的平均價格

創建基本腳本:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System.Linq; // LINQ 需要使用這個庫

[System.Serializable] // 在 inspector 中可見
public class Item
{
    public string name;
    public int id;
    public int price;
}

public class LinqTest : MonoBehaviour
{
    public List<Item> items;

    // Start is called before the first frame update
    void Start()
    {

    }

    // Update is called once per frame
    void Update()
    {
        
    }
}

然後在 inspector 中創建一些 item。

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System.Linq; // LINQ 需要使用這個庫

[System.Serializable] // 在 inspector 中可見
public class Item
{
    public string name;
    public int id;
    public int price;
}

public class LinqTest : MonoBehaviour
{
    public List<Item> items;

    // Start is called before the first frame update
    void Start()
    {
        var result = items.Any(item => item.id == 3);
        Debug.Log("Fond id of 3: " + result);

        var newItems = items.Where(item => item.price > 50);
        foreach(var item in newItems)
        {
            Debug.Log("Item: " + item.name + " Price: " + item.price);
        }

        var total = items.Sum(item => item.price); // 求總價
        float averagePrice = (float)total / items.Count(); // 平均價
        // 為了確保平均價格為浮點數,這裡把 total 設置為 float
        Debug.Log("Average price: " + averagePrice);

        // 也可以直接使用 average:
        var average = items.Average(item => item.price);
        Debug.Log("Average price: " + average);
    }

    // Update is called once per frame
    void Update()
    {
        
    }
}

LINQ: How to Read and Convert Query Syntax

  • LINQ 的另一種使用方法:查詢語法(query syntax)

微軟官方示例:

class LINQQueryExpressions
{
    static void Main()
    {

        // Specify the data source.
        int[] scores = new int[] { 97, 92, 81, 60 };

        // Define the query expression.
        IEnumerable<int> scoreQuery =
            from score in scores
            where score > 80
            select score; // select 語句是必須的

        // Execute the query.
        foreach (int i in scoreQuery)
        {
            Console.Write(i + " ");
        }
    }
}
// Output: 97 92 81

放到 unity 的腳本中:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System.Linq; // LINQ 需要使用這個庫

public class LinqTest : MonoBehaviour
{
    // Specify the data source.
    int[] scores = new int[] { 97, 92, 81, 60 };

    // Start is called before the first frame update
    void Start()
    {
        // Define the query expression.
        IEnumerable<int> scoreQuery =
            from score in scores
            where score > 80
            select score;

        // Execute the query.
        foreach (int i in scoreQuery)
        {
            Debug.Log(i + " ");
        }
    }

    // Update is called once per frame
    void Update()
    {
        
    }
}

這裡使用的就是查詢語法(query syntax),如何轉化為 method syntax 呢:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System.Linq; // LINQ 需要使用這個庫

public class LinqTest : MonoBehaviour
{
    // Specify the data source.
    int[] scores = new int[] { 97, 92, 81, 60 };

    // Start is called before the first frame update
    void Start()
    {
        // Define the query expression.
        /*
        IEnumerable<int> scoreQuery =
            from score in scores
            where score > 80
            select score;
        */
        var scoreQuery = scores.Where(score => score > 80);

        // Execute the query.
        foreach (int i in scoreQuery)
        {
            Debug.Log(i + " ");
        }
    }

    // Update is called once per frame
    void Update()
    {
        
    }
}

推薦使用 method syntax,更簡潔易用。

Tags:
Exit mobile version