淺析LINQ涉及的一些C#語言特性


時間:午飯后

地點:蘇州公司

主題:Entity Framework涉及的一些C#語言特性,要講的語言特性,如下圖所示

1.Extension Methods

很舊的主題了,DebugLZQ以前也有相關的博文提過。用個簡單點的例子來說吧

下面的例子完成一個計算某個月剩余天數的功能,我們可以如下編碼:調用一個靜態幫助類的靜態方法。

using System;

namespace ExtensionMethods
{
    class Program
    {
        static void Main(string[] args)
        {
            DateTime dateTime = new DateTime(2013, 3, 22);
            int daysTillEndOfMonth = DateUtilities.DaysToEndOfMonth(dateTime);
            Console.WriteLine(daysTillEndOfMonth);
            Console.ReadKey();
        }

    }

    public static class DateUtilities
    {
        public static int DaysToEndOfMonth(DateTime dateTime)
        {
            return DateTime.DaysInMonth(dateTime.Year, dateTime.Month) - dateTime.Day;
        }
    }
}

這樣寫當然沒什么問題。很多時候就是這樣用的
如果我們用擴展方法,可以如下實現,注意區別在哪里!

using System;

namespace ExtensionMethods
{
    class Program
    {
        static void Main(string[] args)
        {
            DateTime dateTime = new DateTime(2013, 3, 22);
            int daysTillEndOfMonth = dateTime.DaysToEndOfMonth();
            Console.WriteLine(daysTillEndOfMonth);
            Console.ReadKey();
        }

    }

    public static class DateUtilities
    {
        public static int DaysToEndOfMonth(this DateTime dateTime)
        {
            return DateTime.DaysInMonth(dateTime.Year, dateTime.Month) - dateTime.Day;
        }
    }
}

下面用擴展方法實現的程序明顯比上面的普通函數調用,更容易閱讀。

其實,擴展方法也就是C#的一個語法糖衣,其編譯過后也是方法的調用,其優點是更容易閱讀,因為其可以為一些固有的類如DateTime、String...擴展一些方法。

 2.Query Language

 下面通過上面的擴展方法,來實現數據查詢。可以實現如下:

using System;
using System.Collections.Generic;

namespace ExtensionQuery
{
    class Program
    {
        static void Main(string[] args)
        {
            IEnumerable<string> cities = new[] {"Suzhou","Wuxi","Changzhou","Zhenjiang","Nanjing","Shanghai"};
            IEnumerable<string> query = cities.StringThatStartWith("S");
            
            foreach(var city in query)
            {
                Console.WriteLine(city);
            }
            Console.ReadKey();
        }
    }
    //寫一個擴展方法用迭代器實現過濾
    public static class FilterExtension
    {
        public static IEnumerable<string> StringThatStartWith(this IEnumerable<string> input,string start)
        {
            foreach(var s in input)
            {
                if (s.StartsWith(start))
                    yield return s;
            }
        }
    }
}

上面的代碼運行當然也木有什么問題,但是實現方法有點繁瑣。
可以初步簡化如下:

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

namespace ExtensionQuery
{
    class Program
    {
        static void Main(string[] args)
        {
            IEnumerable<string> cities = new[] {"Suzhou","Wuxi","Changzhou","Zhenjiang","Nanjing","Shanghai"};
            IEnumerable<string> query = cities.StringThatStartWith("S");
            
            foreach(var city in query)
            {
                Console.WriteLine(city);
            }
            Console.ReadKey();
        }
    }
    //
    public static class FilterExtension
    {
        public static IEnumerable<string> StringThatStartWith(this IEnumerable<string> input,string start)
        {
            return input.Where(s => s.StartsWith(start));
        }
    }
}

進一步簡化:

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

namespace ExtensionQuery
{
    class Program
    {
        static void Main(string[] args)
        {
            IEnumerable<string> cities = new[] {"Suzhou","Wuxi","Changzhou","Zhenjiang","Nanjing","Shanghai"};
            IEnumerable<string> query = cities.Where(s => s.StartsWith("S"));

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

不喜歡這種寫法?可以用LINQ來寫,如下:

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

namespace ExtensionQuery
{
    class Program
    {
        static void Main(string[] args)
        {
            IEnumerable<string> cities = new[] {"Suzhou","Wuxi","Changzhou","Zhenjiang","Nanjing","Shanghai"};
            //IEnumerable<string> query = cities.Where(s => s.StartsWith("S"));
            IEnumerable<string> query = from city in cities
                                        where city.StartsWith("S")
                                        select city;
            foreach(var city in query)
            {
                Console.WriteLine(city);
            }
            Console.ReadKey();
        }
    }
}

3.Oh,Holy Shit!

Where從哪里冒出來的!盜用了MS的東西!這里:

IEnumerable<string> query = cities.Where(s => s.StartsWith("S"));//這個“Where”,MS是怎么做到的?自己定義一個Filter怎么樣?

讓我們回到那個擴展方法,從頭來實現 ,如下:

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

namespace ExtensionQuery
{
    class Program
    {
        static void Main(string[] args)
        {
            IEnumerable<string> cities = new[] { "Suzhou", "Wuxi", "Changzhou", "Zhenjiang", "Nanjing", "Shanghai" };
            IEnumerable<string> query = cities.Filter(StringThatStartWith);

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

        static bool StringThatStartWith(string item,string startWord)
        {
            return item.StartsWith(startWord);
        }
    }
    //委托作為擴展方法的參數
    public static class FilterExtension
    {
        public delegate bool FilterDelegate(string item,string startWord);

        public static IEnumerable<string> Filter(this IEnumerable<string> input, FilterDelegate fDelegate)
        {
            foreach (var item in input)
            {
                if (fDelegate(item,"S"))
                    yield return item;
            }
        }
    }
}

 程序運行OK。既然傳入一個委托調用函數可以,那么直接傳入匿名不更簡單,如下:

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

namespace ExtensionQuery
{
    class Program
    {
        static void Main(string[] args)
        {
            IEnumerable<string> cities = new[] { "Suzhou", "Wuxi", "Changzhou", "Zhenjiang", "Nanjing", "Shanghai" };
            //匿名方法簡化
            IEnumerable<string> query = cities.Filter(delegate(string item,string startWord)
                                                          {
                                                              return item.StartsWith(startWord);
                                                          });

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

        //static bool StringThatStartWith(string item,string startWord)
        //{
        //    return item.StartsWith(startWord);
        //}
    }
    //委托作為擴展方法的參數
    public static class FilterExtension
    {
        public delegate bool FilterDelegate(string item,string startWord);

        public static IEnumerable<string> Filter(this IEnumerable<string> input, FilterDelegate fDelegate)
        {
            foreach (var item in input)
            {
                if (fDelegate(item,"S"))
                    yield return item;
            }
        }
    }
}

直接用Lambda來簡化吧,如下:

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

namespace ExtensionQuery
{
    class Program
    {
        static void Main(string[] args)
        {
            IEnumerable<string> cities = new[] { "Suzhou", "Wuxi", "Changzhou", "Zhenjiang", "Nanjing", "Shanghai" };
            //Lambda  
            //至此實現了同“Where”長相一樣的方法!
            IEnumerable<string> query = cities.Filter((item, startWord) => item.StartsWith(startWord));

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

        //static bool StringThatStartWith(string item,string startWord)
        //{
        //    return item.StartsWith(startWord);
        //}
    }
    //委托作為擴展方法的參數
    public static class FilterExtension
    {
        public delegate bool FilterDelegate(string item,string startWord);

        public static IEnumerable<string> Filter(this IEnumerable<string> input, FilterDelegate fDelegate)
        {
            foreach (var item in input)
            {
                if (fDelegate(item,"S"))
                    yield return item;
            }
        }
    }
}

--------
大功告成,利用MS的Where背后的這些C#語法特性,實現了一個類似的Where。 

等等..........

貌似忘了一個東西,對Func<>/Action<>委托啊,干嘛自己定義?

重新寫下,如下:

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

namespace ExtensionQuery
{
    class Program
    {
        static void Main(string[] args)
        {
            IEnumerable<string> cities = new[] { "Suzhou", "Wuxi", "Changzhou", "Zhenjiang", "Nanjing", "Shanghai" };
            //Lambda  
            //至此實現了同“Where”長相一樣的方法!
            IEnumerable<string> query = cities.Filter((item, startWord) => item.StartsWith(startWord));

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

        //static bool StringThatStartWith(string item,string startWord)
        //{
        //    return item.StartsWith(startWord);
        //}
    }
    //委托作為擴展方法的參數
    public static class FilterExtension
    {
        //public delegate bool FilterDelegate(string item,string startWord);
        //用Func<>委托取代自定義的委托
        public static IEnumerable<string> Filter(this IEnumerable<string> input, Func<string,string,bool> fDelegate)
        {
            return input.Where(item => fDelegate(item,"S"));
        }
    }
}

 這下OK了

4.Lambda Expressions 

Lambda表達式可以有多個參數、一個參數,或者沒有參數。其參數類型可以隱式或者顯式。示例代碼如下:

  1. (x, y) => x * y //多參數,隱式類型=> 表達式
  2. x => x * 5 //單參數, 隱式類型=>表達式
  3. x => { return x * 5; } //單參數,隱式類型=>語句塊
  4. (int x) => x * 5 //單參數,顯式類型=>表達式
  5. (int x) => { return x * 5; } //單參數,顯式類型=>語句塊
  6. () => Console.WriteLine() //無參數

上述格式都是Lambda表達式的合法格式,在編寫Lambda表達式時,可以忽略參數的類型,因為編譯器能夠根據上下文直接推斷參數的類型。

5.Func<T>和Expression<T>

 這個源自園子里的一個博問,問題如下:

Func<TObject, bool>是委托(delegate)

Expression<Func<TObject, bool>>是表達式

Expression編譯后就會變成delegate,才能運行。比如

Expression<Func<int, bool>> ex = x=>x < 100;

Func<int, bool> func = ex.Compile();

然后你就可以調用func:

func(5) //-返回 true

func(200) //- 返回 false

而表達式是不能直接調用的。

參考:http://stackoverflow.com/questions/793571/why-would-you-use-expressionfunct-rather-than-funct

 

關於EF中用哪個你可以看看這篇文章:Entity Framework - Func引起的數據庫全表查詢

關於如何將多個expression合並為一個可以寫多個where:

.where(expression1).where(expression2)...

運行時EF會自動合並優化的

 小結

1.下面兩種寫法都行

IEnumerable<string> cities = new[] { "Suzhou", "Wuxi", "Changzhou", "Zhenjiang", "Nanjing", "Shanghai" };
//IEnumerable<string> cities = new string[] { "Suzhou", "Wuxi", "Changzhou", "Zhenjiang", "Nanjing", "Shanghai" };//OK also

構建一個可枚舉的集合,這是新特性,逆變與協變(和“父類可以替換子類”相似)。

2.類似“Where”的語法如何自己構建。
3.Lambda表達式的寫法。譬如說{}。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM