LINQ查詢運算式 - Select、Cast、OfType

 

一、查詢運算式結構

linq syntax

 

各部分的查詢運算式為

範圍變數:該變數位置在from後面的變數,在某方面不算是一種變數,他只能用在查詢運算式上,

主要是存放目前所擷取到的元素資料,並可用來提供其他查詢運算式使用。

 

基本的查詢運算式為

var query = from user in SampleData.AllUsers select user.Name;

其作用相等於

var query = SampleData.AllUsers.Select(user => user.Name);

 

二、Select

using System;
using System.Collections.Generic;
using System.Linq;

namespace ConsoleApp1
{
    class Program
    {
        static void Main(string[] args)
        {
            SampleData data = new SampleData();
            var query = from user in SampleData.AllUsers
                        select user;

            foreach (var item in query)
            {
                Console.WriteLine(item);
            }
        }
    }

    public class SampleData
    {
        static List<User> users;
        public static IEnumerable<User> AllUsers
        {
            get { return users; }
        }

        public static class Users
        {
            public static readonly User TesterTim = new User("Tim Trotter", UserType.Tester);
            public static readonly User TesterTara = new User("Tara Tutu", UserType.Tester);
            public static readonly User DeveloperDeborah = new User("Deborah Denton", UserType.Developer);
            public static readonly User DeveloperDarren = new User("Darren Dahlia", UserType.Developer);
            public static readonly User ManagerMary = new User("Mary Malcop", UserType.Manager);
            public static readonly User CustomerColin = new User("Colin Carton", UserType.Customer);
        }

        public SampleData()
        {
            users = new List<User>
            {
                Users.TesterTim,
                Users.TesterTara,
                Users.DeveloperDeborah,
                Users.DeveloperDarren,
                Users.ManagerMary,
                Users.CustomerColin
            };
        }
    }

    public class User
    {
        public string Name { get; set; }
        public UserType UserType { get; set; }

        public User(string name, UserType userType)
        {
            Name = name;
            UserType = userType;
        }

        public override string ToString()
        {
            return string.Format("User: {0} ({1})", Name, UserType);
        }
    }

    public enum UserType : byte
    {
        Customer,
        Developer,
        Tester,
        Manager,
    }

}

執行結果為

 

Select 通常又稱為投影 projection,

可以透過他來將原始的型別轉換成新的型別,

如:從IEnumerable<SampleData.AllUser>的集合中,

選出 user.Name 並包裝成 lEnumerable<string> 集合回傳。

var query = from user in SampleData.AllUsers
            select user.Name;

上例select方法只能取得單一屬性,如果想取多個屬性,

則可以包裝成物件,如下例為使用匿名型別(Anonymous Type)來重新建立匿名物件。

var query = from user in SampleData.AllUsers
            select new
            {
                Name = user.Name,
                Length = user.Name.Length
            };

也可以直接使用投影出來的屬性來做成匿名物件

var query = from user in SampleData.AllUsers
            select new { user.Name, user.Name.Length };

或是使用Tuple 類別

var query = from user in SampleData.AllUsers
            select new Tuple<string, int>(user.Name, user.Name.Length);

另外一種表示法為使命名物件

using System;
using System.Collections.Generic;
using System.Linq;

namespace ConsoleApp1
{
    class Program
    {
        static void Main(string[] args)
        {
            SampleData data = new SampleData();
            var query = from user in SampleData.AllUsers
                        select new NameLengrth { name = user.Name, Length = user.Name.Length };

            foreach (var item in query)
            {
                Console.WriteLine(item);
            }
        }
    }

    class NameLengrth
    {
        public string name { get; set; }
        public int Length { get; set; }
    }

    public class SampleData
    {
        //...
    }

    public class User
    {
        //...
    }

    public enum UserType : byte
    {
        //...
    }

}

注意,NameLengrth是一個類別(class)

這種表示法也行

var query = from user in SampleData.AllUsers
            select new NameLengrth() { name = user.Name, Length = user.Name.Length };

 

三、Cast、OfType

Cast 會將集合內所有元素轉換成目標型別,若有集合無法轉換的型別,

這時候會拋出 InvalidCastException 的例外;

Oftype 會將集合內所有元素依依轉換成目標型別,若有集合無法轉換的型別,

這時候會忽略。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Collections;

namespace Linqtest
{
    class Program
    {
        static void Main(string[] args)
        {
            ArrayList list = new ArrayList { "First","Second","Third"};
            IEnumerable<string> strings = list.Cast<string>();
            foreach (var item in strings)
            {
                Console.WriteLine(item);
            }

            list = new ArrayList { 1, "not a int", 2, 3 };
            IEnumerable<int> ints = list.OfType<int>();

            foreach (var item in ints)
            {
                Console.WriteLine(item);
            }

            Console.ReadKey();
        }
    }
}

 

再看一個例子

using System;
using System.Collections.Generic;
using System.Linq;
using System.Collections;

namespace Linqtest
{
    class Program
    {
        static void Main(string[] args)
        {
            ArrayList list = new ArrayList { "First","Second","Third"};

            var strings = from string entry in list
                          select entry;

            foreach (var item in strings)
            {
                Console.WriteLine(item);
            }

            Console.ReadKey();
        }
    }
}

使用明確型別的範圍變數時,編譯器會去呼叫 Cast 擴充方法,

以確保目前查詢運算式的來源資料是可以被轉換範圍變數的型別。

 

參考資料:

A Visual Explanation of SQL Joins

C# in Depth

[C#.NET] 如果可以 請盡量使用LINQ查詢語法而不是使用迴圈