C#學習筆記8

来源:http://www.cnblogs.com/zwt-blog/archive/2017/01/19/6306227.html
-Advertisement-
Play Games

1.泛型的約束: (1)介面約束; (2)基類約束,基類約束必須放在第一(假如有多個約束); (3)struct/class約束; (4)多個參數類型的約束,每個類型參數都要用where關鍵字; (5)構造器約束,只能是無參構造器,如new(); (6)約束可以由派生類繼承,但必須在派生類中顯式地指 ...


1.泛型的約束:

(1)介面約束;

(2)基類約束,基類約束必須放在第一(假如有多個約束);

(3)struct/class約束;

(4)多個參數類型的約束,每個類型參數都要用where關鍵字;

(5)構造器約束,只能是無參構造器,如new();

(6)約束可以由派生類繼承,但必須在派生類中顯式地指定這些約束;

(7)泛型方法的約束設置與泛型類的約束設置,是一樣的;

2.協變性與逆變性:在泛型中,將一個較具體的類型賦給一個較泛化的類型,即是協變。將一個較泛化的類型賦給一個較具體的類型,即是逆變。

  協變:在C#4.0中使用out類型參數修飾符允許協變性,該out修飾的類型參數,會導致該類型參數只能用於成員的返回與屬性的取值方法,永遠不用於輸入參數或者屬性的賦值方法。

  逆變:在C#4.0中使用in類型參數修飾符允許逆變性,該in修飾的類型參數,會導致該類型參數只能用於成員的輸入(輸入參數)與屬性的設置方法。

  如Contact類繼承自PdaItem,若對泛型介面(IConvertible<in T1,out T2>)進行了協變與逆變的設置,就可以成功地從一個IConvertible<PdaItem,Contact>轉換成一個IConvertible<Contact,PdaItem>。

綜上所述:協出逆進。

3.數組本身是支持協變與逆變,如PdaItem[] pdaItems=new Contact[]{},Contact[] pdaItems=(Contact)new PdaItem[]{}。

4.委托概述:長期以來經驗豐富的C、C++程式員利用方法指針,將可執行的步驟(方法)作為參數傳遞給另一個方法。C#使用委托來提供相同的功能,它將方法作為對象封裝起來,允許在運行時間接綁定一個方法的調用,可查看DelegateSample類的代碼。

5.委托的定義:C#將所有委托的定義成間接派生於System.Delegate,其繼承層次為Object -> Delegate ->MulticastDelegate -> 自定義委托。當然我們使用關鍵delegate聲明,其在編譯後生成的CIL代碼,就是自動繼承上面的結構,因此我們不能手動顯式繼承委托。在委托實例化中,從[email protected]開始可以不使用new來手工創建,直接賦值相同的簽名方法即可,編譯器會在編譯過程中會自動根據委托推斷,自動添加new創建,以及把方法名稱作為委托參數傳遞也是一樣,

6.系統自定義的委托:在.net3.5(C#3.0)中加入了Action與Func這個兩個泛型委托,前者是沒有返回值的委托,後者是有返回值的委托,在.Net4.0(C#4.0)中又在這些泛型委托中加入了in(逆變)/out(協變)的功能。這些委托的定義省略了我們手工進行自定義委托的情況,因為這些泛型委托都涵蓋了我們可能遇到的所有使用情況。(除非你要定義一個有特殊含義的委托名稱)。

7.委托使用可變性,即委托類型參數的可變性(協變與逆變),可查看DelegateSample.CovariantAndContravariant()方法的代碼。

8.Lambda表達式:Lambda表達式是匿名函數的簡潔表達形式,Lambda表達式包含語句Lambda與表達式Lambda,前者可以含有多個語句的語句塊(一般只使用兩三條語句),後者只有一句表達式。

9.使用Lambda表達式註意事項:

(1)Lambda表達式本身是無類型,只有賦值給一個委托,Lambda表達式就表現為有一個類型,就是該方法簽名的委托。

(2)因Lambda表達式是無類型的,所以也就不能賦值給隱式類型的局部變數。

(3)如果目的地在Lambda表達式的外部,C#就不允許在匿名函數內部使用跳轉語句(break、goto、continue),類似的,不能從Lambda表達式外部跳入Lambda表達式內部。

(4)對於在Lambda表達式內部引入的變數,其作用域僅限於Lambda表達式主體。

(5)編譯器的程式執行流程分析機制檢測不到在Lambda表達式內部初始化局部變數的情況。

  以上的註意事項的代碼實例,可以查看DelegateSample.NoticeMatter()方法。

10.Lambda表達式並非CLR內部的固有構造,它們的實現是由C#編譯器在編譯時生成的,編譯器會自動把Lambda表達式生成為一個當前類的內部方法(靜態或實例方法),其方法名是編譯器自動處理(其一般含有anonymous等字眼的描述)。

11.在匿名函數中使用外部局部變數,編譯器會把這些外部變數生成為當前類的嵌套類(sealed閉包)的公共欄位,其嵌套類中含有公共欄位與對應的匿名名稱方法,因此這些局部變數變成了嵌套的成員,其生命周期也就被延長了。通過查看ILdasm(中間語言反編譯,能夠查看類庫編譯後的中間語言,VS工具自帶)。也可查看DelegateSample.AscendingAndTimeOfCompiler()與_LocalDisplayClass_00001嵌套類。

12.表達式樹:“解釋”是C#引入表達式樹這一概念的重要動機,如果一種Lambda表達式代表的是與表達式有關的數據,而不是編譯好的代碼(匿名函數與委托),這種Lambda表達式就是“表達式樹”。由於表達式樹代表的是數據而非編譯好的代碼,所以可以把數據轉換成一種替代格式。

  例如,可以把它從表達式數據轉換成在資料庫中執行的SQL代碼。如IQueryable介面中含有表達式樹類型、查詢供應者、查詢返回的類型,只要實現該介面就能對特定的數據源進行查詢操作,期內包含需要自定義解析表達式樹的指令。所有表達式樹的類型(如ParameterExpression、BinaryExpression、MethodCallExpression),都是從Expression繼承。

13.Lambda表達式和表達式樹的比較:無論是用於委托的Lambda表達式,還是用於表達式樹的Lambda表達式都會被編譯,而且在這兩種情況下,表達式的語法都會在編譯時進行完整的語義分析驗證。但是,兩者的區別在於,Lambda表達式在CIL中遍編譯成委托,而表達式樹被編譯成System.Linq.Expressions.Expression類型的一個數據結構。可查看AnalysisExpression類的代碼,以及Output()方法輸出的數據內容。

14.一個表達式樹是由零個或者多個其他表達式樹構成的,被包含的表達式樹存儲在Expression的Body屬性中。通常,語句Lambda和表達式Lambda可以互換使用,然而,不能將語句Lambda轉換成“表達式樹”,只能使用表達式Lambda語法來表示“表達式樹”。

public class DelegateSample
{
    //冒泡排序,基本排序處理
    public static void BubbleSort<TSource>(TSource[] items, Func<TSource, TSource, bool> predicate) where TSource : IComparable<TSource>, IEquatable<TSource>
    {
        if (items == null || items.Length < 2)
        {
            return;
        }
        TSource tempItem;
        /*第一種演算法,定點比較
        for (int i = 0; i < items.Length; i++)
        {
            for (int j = i; j < items.Length; j++)
            {
                if (predicate(items[i], items[j]))
                {
                    tempItem = items[i];
                    items[i] = items[j];
                    items[j] = tempItem;
                }
            }
        }*/
        //第二種演算法,逐一比較
        for (int i = items.Length - 1; i >= 0; i--)
        {
            for (int j = 1; j <= i; j++)
            {
                if (predicate(items[j - 1], items[j]))
                {
                    tempItem = items[j - 1];
                    items[j - 1] = items[j];
                    items[j] = tempItem;
                }
            }
        }
    }

    public static void Ascending()
    {
        int[] items = new int[] { 3, 2, 1, 4, 6, 5 };
        Console.WriteLine("升序前");
        foreach (var item in items)
        {
            Console.Write("{0}\t", item);
        }
        BubbleSort(items, (first, second) => first > second);
        Console.WriteLine("\n升序後");
        foreach (var item in items)
        {
            Console.Write("{0}\t", item);
        }
    }

    public static void Descending()
    {
        int[] items = new int[] { 3, 2, 1, 4, 6, 5 };
        Console.WriteLine("降序前");
        foreach (var item in items)
        {
            Console.Write("{0}\t", item);
        }
        BubbleSort(items, (first, second) => first < second);
        Console.WriteLine("\n降序後");
        foreach (var item in items)
        {
            Console.Write("{0}\t", item);
        }
    }
    //按字母排序
    public static void AlphabeticalGreaterThan()
    {
        int[] items = new int[] { 1, 12, 13, 5, 4, 6 };
        Console.WriteLine("排序前");
        foreach (var item in items)
        {
            Console.Write("{0}\t", item);
        }
        BubbleSort(items, (first, second) =>
        {
            int comparison = first.ToString().CompareTo(second.ToString());
            return comparison > 0;
        });
        Console.WriteLine("\n排序後");
        foreach (var item in items)
        {
            Console.Write("{0}\t", item);
        }
    }
    //委托的協變與逆變
    public static void CovariantAndContravariant()
    {
        //逆變
        Action<object> broadAction = delegate (object data) { Console.WriteLine(data); };
        Action<string> narrowAction = broadAction;

        //協變
        Func<string> narrowFunction = () => Console.ReadLine();
        Func<object> broadFunction = narrowFunction;

        //協變與逆變
        Func<object, string> func1 = (data) => data.ToString();
        Func<string, object> func2 = func1;
    }
    //Lambda表達式註意事項
    public static void NoticeMatter()
    {
        //錯誤:控制權不能離開Lambda表達式的主體。
        /*string[] args;
        Func<string> expression;
        switch (args[0])
        {
            case "/File":
                expression = () =>
                {
                    if (!File.Exists(args[1]))
                    {
                        break;
                    }
                    //...
                    return args[1];
                };
                //...
        }*/

        //不能在Lambda表達式內部進行初始化局部變數。
        /*int number;
        Func<string, bool> expression = text => int.TryParse(text, out number);
        if (expression("1"))
        {
            //錯誤:使用未賦值的局部變數
            Console.WriteLine(number);
        }

        int number:
        Func<int, bool> isFortyTwo = x => 42 == (number = x);
        if (isFortyTwo(42))
        {
            //錯誤:使用未賦值的局部變數
            Console.WriteLine(number);
        }*/
    }

    //升序,並記錄其比較的次數,編譯前
    public static void AscendingAndTime()
    {
        int comparisonCount = 0;
        int[] items = new int[] { 3, 2, 1, 4, 6, 5 };
        Console.WriteLine("升序前");
        foreach (var item in items)
        {
            Console.Write("{0}\t", item);
        }
        BubbleSort(items, (first, second) =>
        {
            comparisonCount++;
            return first > second;
        });
        Console.WriteLine("\n升序後");
        foreach (var item in items)
        {
            Console.Write("{0}\t", item);
        }
        Console.WriteLine("\n比較的次數 {0}", comparisonCount);
    }

    //升序,並記錄其比較的次數,編譯後
    public static void AscendingAndTimeOfCompiler()
    {
        _LocalDisplayClass_00001 locals = new _LocalDisplayClass_00001();
        locals.comparisonCount = 0;
        int[] items = new int[] { 3, 2, 1, 4, 6, 5 };
        Console.WriteLine("升序前");
        foreach (var item in items)
        {
            Console.Write("{0}\t", item);
        }
        BubbleSort(items, locals._AnonymousMethod_000000);
        Console.WriteLine("\n升序後");
        foreach (var item in items)
        {
            Console.Write("{0}\t", item);
        }
        Console.WriteLine("\n比較的次數 {0}", locals.comparisonCount);
    }

    private sealed class _LocalDisplayClass_00001
    {
        public int comparisonCount;

        public bool _AnonymousMethod_000000(int first, int second)
        {
            comparisonCount++;
            return first > second;
        }
    }
}

public class AnalysisExpression
{
    //輸出表達式樹的數據結構
    public void Output()
    {
        Expression<Func<int, int, bool>> expression = (x, y) => x > y;
        Console.WriteLine("------------{0}------------", expression);
        PrintNode(expression.Body, 0);
        Console.WriteLine("\n");
        expression = (x, y) => x * y > x + y;
        Console.WriteLine("------------{0}------------", expression);
        PrintNode(expression.Body, 0);
        Console.WriteLine("\n");
    }
    //列印表達式樹節點,一般表達式樹
    private void PrintNode(Expression expression, int indent)
    {
        if (expression is BinaryExpression)
        {
            PrintNode(expression as BinaryExpression, indent);
        }
        else
        {
            PrintSingle(expression, indent);
        }
    }
    //列印表達式樹節點,二元表達式樹
    private void PrintNode(BinaryExpression expression, int indent)
    {
        //表達式樹是一個數據集合,而通過遍曆數據,就可以將數據轉換成另一種格式。此處將數據直接轉換成對應的文本,然而,具體如何對數據進行解釋,完全是開發者自己決定。
        PrintNode(expression.Left, indent + 1);
        PrintSingle(expression, indent);
        PrintNode(expression.Right, indent + 1);
    }
    //列印單個表達式樹節點內容
    private void PrintSingle(Expression expression, int indent)
    {
        Console.WriteLine("{0," + indent * 5 + "}{1}", "", NodeToString(expression));
    }
    //節點轉到字元
    private string NodeToString(Expression expression)
    {
        string content;
        switch (expression.NodeType)
        {
            case ExpressionType.Multiply:
                content = "*";
                break;
            case ExpressionType.Add:
                content = "+";
                break;
            case ExpressionType.Divide:
                content = "/";
                break;
            case ExpressionType.Subtract:
                content = "-";
                break;
            case ExpressionType.GreaterThan:
                content = ">";
                break;
            case ExpressionType.LessThan:
                content = "<";
                break;
            default:
                content = string.Format("{0}({1})", expression, expression.NodeType);
                break;
        }
        return content;
    }
}
View Code

---------------------以上內容根據《C#本質論 第三版》進行整理


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 這裡說的“後門”並不是教你做壞事,而是讓你做好事,搭建自己的調試工具更好地進行調試開發。我們都知道,當程式發生異常錯誤時,我們需要定位到錯誤,有時我們還想,我們在不修改程式的前提下,就能通過log來定位錯誤呢?有人會說,我在我的程式裡加多點列印就好了,程式每做一步我就加一行列印,到時一查log就知道 ...
  • 1 比較nor/nand flash NOR NAND介面: RAM-Like,引腳多 引腳少,復用容量: 小 1M 2M 3M 大:128M 256M G讀: 簡單 複雜寫: 發出特定命令 慢 發出特定命令 快價格: 貴 便宜特點: 無位反轉、壞塊 位反轉、壞塊 關鍵重要的程式 大數據、容忍可以出 ...
  • 1 nand flash的操作 目的:讀地址A的數據,把數據B寫到地址A。 問1. 原理圖上NAND FLASH和S3C2440之間只有數據線,怎麼傳輸地址?答1.在DATA0~DATA7上既傳輸數據,又傳輸地址,當ALE為高電平時傳輸的是地址。 問2. 從NAND FLASH晶元手冊可知,要操作N ...
  • 1.桌面右擊新建txt文件複製下麵兩行代碼,修改文件尾碼名為bat保存文件 netsh wlan set hostednetwork mode=allow ssid=zhangxh key=xiaoheng123netsh wlan start hostednetwork 2.右擊bat文件以管理員 ...
  • 1 塊設備的概述 linux支持的兩種重要的設備類型分別是字元設備和塊設備,塊設備可以隨機地以固定大小的塊傳送數據。與字元設備相比,塊設備有以下幾個特殊之處: (1)塊設備可以從數據的任何位置進行訪問 (2)塊數據總是以固定長度進行傳輸,即便請求的這是一個位元組 (3)對塊設備的訪問有大量的緩存。當進 ...
  • 去埠號功能主要用於Apache與IIS等WEB伺服器共存時,去除功能變數名稱後面所帶的埠 本文案例採用我開發的純綠色PHP集成環境PHPWAMP裡面的“去埠”功能模塊。 案例演示: 點擊常用工具,打開“去掉功能變數名稱非80埠”功能即可 彈出的界面菜單如下圖 如下填寫,功能變數名稱填寫格式abc.com,具體如下圖 ...
  • 最近有學生向我咨詢如何同時建立多個不同PHP版本站點,並自定義任意版本,軟體是否可以多開,PHPWAMP如何設置才能與其他的環境同時使用等問題,本文將一一解決。 簡單介紹一下PHPWAMP 你們應該會經常聽到WAMP這詞吧,那麼WAMP是什麼意思? Windows下的Apache+Mysql+PHP ...
  • phpwamp在伺服器搭建網站,php網站在伺服器上的搭建方式,雲伺服器上如何使用PHP綠色集成環境 ...
一周排行
    -Advertisement-
    Play Games
  • 前言 本文介紹一款使用 C# 與 WPF 開發的音頻播放器,其界面簡潔大方,操作體驗流暢。該播放器支持多種音頻格式(如 MP4、WMA、OGG、FLAC 等),並具備標記、實時歌詞顯示等功能。 另外,還支持換膚及多語言(中英文)切換。核心音頻處理採用 FFmpeg 組件,獲得了廣泛認可,目前 Git ...
  • OAuth2.0授權驗證-gitee授權碼模式 本文主要介紹如何筆者自己是如何使用gitee提供的OAuth2.0協議完成授權驗證並登錄到自己的系統,完整模式如圖 1、創建應用 打開gitee個人中心->第三方應用->創建應用 創建應用後在我的應用界面,查看已創建應用的Client ID和Clien ...
  • 解決了這個問題:《winForm下,fastReport.net 從.net framework 升級到.net5遇到的錯誤“Operation is not supported on this platform.”》 本文內容轉載自:https://www.fcnsoft.com/Home/Sho ...
  • 國內文章 WPF 從裸 Win 32 的 WM_Pointer 消息獲取觸摸點繪製筆跡 https://www.cnblogs.com/lindexi/p/18390983 本文將告訴大家如何在 WPF 裡面,接收裸 Win 32 的 WM_Pointer 消息,從消息裡面獲取觸摸點信息,使用觸摸點 ...
  • 前言 給大家推薦一個專為新零售快消行業打造了一套高效的進銷存管理系統。 系統不僅具備強大的庫存管理功能,還集成了高性能的輕量級 POS 解決方案,確保頁面載入速度極快,提供良好的用戶體驗。 項目介紹 Dorisoy.POS 是一款基於 .NET 7 和 Angular 4 開發的新零售快消進銷存管理 ...
  • ABP CLI常用的代碼分享 一、確保環境配置正確 安裝.NET CLI: ABP CLI是基於.NET Core或.NET 5/6/7等更高版本構建的,因此首先需要在你的開發環境中安裝.NET CLI。這可以通過訪問Microsoft官網下載並安裝相應版本的.NET SDK來實現。 安裝ABP ...
  • 問題 問題是這樣的:第三方的webapi,需要先調用登陸介面獲取Cookie,訪問其它介面時攜帶Cookie信息。 但使用HttpClient類調用登陸介面,返回的Headers中沒有找到Cookie信息。 分析 首先,使用Postman測試該登陸介面,正常返回Cookie信息,說明是HttpCli ...
  • 國內文章 關於.NET在中國為什麼工資低的分析 https://www.cnblogs.com/thinkingmore/p/18406244 .NET在中國開發者的薪資偏低,主要因市場需求、技術棧選擇和企業文化等因素所致。歷史上,.NET曾因微軟的閉源策略發展受限,儘管後來推出了跨平臺的.NET ...
  • 在WPF開發應用中,動畫不僅可以引起用戶的註意與興趣,而且還使軟體更加便於使用。前面幾篇文章講解了畫筆(Brush),形狀(Shape),幾何圖形(Geometry),變換(Transform)等相關內容,今天繼續講解動畫相關內容和知識點,僅供學習分享使用,如有不足之處,還請指正。 ...
  • 什麼是委托? 委托可以說是把一個方法代入另一個方法執行,相當於指向函數的指針;事件就相當於保存委托的數組; 1.實例化委托的方式: 方式1:通過new創建實例: public delegate void ShowDelegate(); 或者 public delegate string ShowDe ...