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
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...