C# 3.0-c#5.0 變化


最近發現對於C#的使用水平一只停留在3.0的程度 對於4.0 5.0的新特性使用的很少,寫一篇文章記錄一下增加一下認識。

C# 3.5

擴展方法

擴展方法所在的類和擴展方法必須是靜態的 並且擴展方法第一個參數是要擴展的類名 並在this

Person person = new Person();

public static class Extension
    {  
        public static void ExtensionMethod(this Person p)
        {
            .......;
        }
}

person.ExtensionMethod();

當person調用 ExtensionMethod()時,C# Compiler 會把這段Source Code 編譯成Static Method 的IL Code。也可以理解為

編譯器做了這樣的處理,person.ExtensionMethod();  =>  ExtensionMethod(person);

C# Compiler 編譯的過程是這樣的:

  • 首先查看Person類中是否有ExtensionMethod()方法,有的話,直接調用;
  • 如果沒有,在相應的NameSpace 當中,查找static Class,看看這些static Class當中,是否有public static void ExtensionMethod(this Person p) 這樣的方法,如果也沒有,編譯通不過。

c#4.0

弱引用

我們平常用的都是對象的強引用,如果有強引用存在,GC是不會回收對象的。我們能不能同時保持對對象的引用,而又可以讓GC需要的時候回收這個對象呢?.NET中提供了WeakReference來實現。弱引用可以讓您保持對對象的引用,同時允許GC在必要時釋放對象,回收內存。對於那些創建便宜但耗費大量內存的對象,即希望保持該對象,又要在應用程序需要時使用,同時希望GC必要時回收時,可以考慮使用弱引用。弱引用使用起來很簡單,看下面的代碼:
Object obj = new Object();
WeakReference wref = new WeakReference( obj );
obj = null;
第一行代碼新建了一個新的對象,這里叫它對象A,obj是對對象A的強引用。接着第二行代碼新建了一個弱引用對象,參數就是對象A的強引用,第三行代碼釋放掉對對象A的強引用。這時如果GC進行回收,對象A就會被回收。
怎樣在取得對象A的強引用呢?很簡單,請看代碼2:
Object obj2 = wref.Target;
if( obj2 != null )
{
   // 做你想做的事吧。
}
else
{
// 對象已經被回收,如果要用必須新建一個。
}
只要顯示的將弱引用的Target屬性附值就會得到弱引用所代表對象的一個強引用。不過在使用對象之前要對其可用性進行檢查,因為它可能已經被回收了。如 果你得到的是null(VB.NET下為Nothing),表明對象已經被回收,不能再用了,需要重新分配一個。如果不是null,就可以放心大膽的用 了。
接下來讓我們看WeakReference的另外一個版本,請看代碼3:
// public WeakReference(
//   object target,
//   bool trackResurrection
//);
Object obj1 = new Object();
Object obj2 = new Object();
WeakReference wref1 = new WeakReference( obj1, false );
WeakReference wref2 = new WeakReference( obj2, true );
WeakReference的另外一個版本有兩個參數,第一個參數和我們前面用的版本的一樣。第二個參數讓我們看一下他的原型,bool trackResurrection,跟蹤復活,是個bool型,就是是否跟蹤復活。前面的文章中我提到過需要Finalize的對象在最終釋放前會有一 次復活,我們大概可以猜到第二個參數表示的意思了。如果我們第二個參數給false,這個弱引用就是一個short weak reference(短弱引用),當GC回收時,發現根中沒有這個對象的引用了,就認為這個對象無用,這時短弱引用對這個對象的跟蹤到此為止,弱引用的 Target被設置為null。前面的一個參數的構造函數版本新建的弱引用為短弱引用。如果第二個參數給true,這個弱引用就是一個long weak reference(長弱引用)。在對象的Finalize方法沒有被執行以前,Target都可用。不過這是對象的某些成員變量也許已經被回收,所以使 用起來要想當小心。
現在讓我們看看WeakReference是如何實現的。很顯然WeakReference不能直接的引用目標對象,WeakReference的 Target屬性的get/set是兩個函數,從某處查到目標對象的引用返回,而不是我們最常用寫的那樣直接返回或者設置一個私有變量。GC維護了兩個列 表來跟蹤兩種弱引用的目標對象,在一個 WeakReference對象創建時,它在相應的列表中找到一個位置,將目標對象的引用放入,很顯然,這兩個列表不是根的一部分。在GC進行內存回收的 時候,如果要回收某一個對象,會檢查弱引用的列表,如果保存着這個對象的引用,則將其設為null。

可選參數方法

帶有可選參數方法的聲明:

public StreamReader OpenTextFile(string path,Encoding encoding = null,bool detectEncoding = true,int bufferSize = 1024);

命名參數必須在最后使用:

OpenTextFile("foo.txt", Encoding.UTF8, bufferSize: 4096);

順序不限:

OpenTextFile(bufferSize: 4096, path: "foo.txt", detectEncoding: false);

Lazy<T>

我們也許會遇到這樣一種情況,我們有一個大家伙(大對象)需要創建,那么這個對象的創建時需要較長的時間,同時也需要在托管堆上分配較多的空間。

那么在.NET Framework 4 中提供了這樣一個很聰明的方式:Lazy<T>(我們可以稱之為懶對象)。當然,在之前,很多人也曾對其進行過自己的實現。

那么我們在這里就可以把 Lazy<T> 的作用總結為一句話,按需延遲加載

內存映射文件

<?XML:NAMESPACE PREFIX = [default] http://www.w3.org/1999/xhtml NS = "http://www.w3.org/1999/xhtml" />利用文件與內存空間之間的映射,應用程序(包括多個進程)可以通過直接在內存中進行讀寫來修改文件。 從 .NET Framework 4 版開始,可以使用托管代碼按照本機 Windows 函數訪問內存映射文件的方式來訪問內存映射文件,如 MSDN Library 中的 Managing Memory-Mapped Files in Win32(管理 Win32 中的內存映射文件)中所述。

動態語言運行時

4.0中增加了與編譯器相關的API,這樣就可以將字符串作為代碼動態編譯執行,跟javascript好像。借助於 DLR,可以更輕松地開發要在 .NET Framework 上運行的動態語言,而且向靜態類型化語言添加動態功能也會更容易。

動態語言可以在運行時標識對象的類型,而在類似 C# 和 Visual Basic 的靜態類型化語言中(當您使用 Option Explicit On 時),您必須在設計時指定對象類型。 動態語言的示例有:Lisp、Smalltalk、JavaScript、PHP、Ruby、Python、ColdFusion、Lua、Cobra 和 Groovy。

大多數動態語言都會向開發人員提供以下優點:

  • 可以使用快速反饋循環(REPL 或讀取-計算-打印循環)。 這樣,您就可以在輸入幾條語句之后立即執行它們以查看結果。

  • 同時支持自上而下的開發和更傳統的自下而上的開發。 例如,當您使用自上而下的方法時,可以調用尚未實現的函數,然后在需要時添加基礎實現。

  • 更易於進行重構和代碼修改操作,原因是您不必在代碼中四處更改靜態類型聲明。

利用動態語言可以生成優秀的腳本語言。 利用新的命令和功能,客戶可以輕松地擴展使用動態語言創建的應用程序。 動態語言還經常用於創建網站和測試工具、維護服務器場、開發各種實用工具以及執行數據轉換。

DLR 的目的是允許動態語言系統在 .NET Framework 上運行,並為動態語言提供 .NET 互操作性。 在 Visual Studio 2010 中,DLR 將動態對象引入到 C# 和 Visual Basic 中,以便這些語言能夠支持動態行為,並且可以與動態語言進行互操作。

DLR 還可幫助您創建支持動態操作的庫。 例如,如果您具有一個使用 XML 或 JavaScript 對象表示法 (JSON) 對象的庫,則對於使用 DLR 的語言,您的對象可以顯示為動態對象。 這使庫用戶能夠編寫語法更簡單且更自然的代碼,以便操作對象和訪問對象成員。

例如,在 C# 中,您可能會使用下面的代碼來遞增 XML 中的計數器值。

Scriptobj.SetProperty("Count", ((int)GetProperty("Count")) + 1);

通過使用 DLR,您可以改用下面的代碼來執行相同的操作。

scriptobj.Count += 1;

與 CLR 類似,DLR 是 .NET Framework 的一部分,並隨 .NET Framework 和 Visual Studio 安裝包一起提供。 DLR 的開放源代碼版本還可以從 CodePlex 網站下載獲得。

C#4.0加入了dynamic關鍵字,可以申明一個 變量的static類型為dynamic。
在3.0及之前,如果你不知道一個 變量的類型,而要去調用它的一個方法,一般會用到反射:
object calc = GetCalculator();
Type calcType = calc.GetType();
object res = calcType.InvokeMember("Add",BindingFlags.InvokeMethod, null,new object[] { 10, 20 });int sum = Convert.ToInt32(res);
有了dynamic,就可以把上面代碼簡化為:
dynamic calc = GetCalculator();
int sum = calc.Add(10, 20);
使用dynamic的好處在於,可以不去關心對象是來源於COM, IronPython, HTML DOM或者反射,只要知道有什么方法可以調用就可以了,剩下的工作可以留給runtime。
dynamic也可以用在 變量的傳遞中,runtime會自動選擇一個最匹配的overload方法。
這里有一個demo:把一段javascript代碼拷到C#文件中,將var改成dynamic,function改成void,再改一下 構造函數的調用方式(new type()改為win.New.type()),去掉javascript中的win.前綴(因為這已經是C#的方法了),就可以直接運行了。
dynamic的實現是基於IDynamicObject接口和DynamicObject 抽象類。而動態方法、屬性的調用都被轉為了GetMember、Invoke等方法的調用。

 

 

泛型中的協變和逆變

在C#中,下面的類型轉換是非法的:

IList<string> strings = new List<string>();

IList<object> objects = strings;

因為你有可能會這樣做,而編譯器的靜態檢查無法查出錯誤:

objects[0] = 5;

string s = strings[0];

4.0中在聲明generic的Interface及Delegate時可以加in及out關鍵字,如:

public interface IEnumerable<out T> : IEnumerable{IEnumerator<T> GetEnumerator();}public interface IEnumerator<out T> : IEnumerator{bool MoveNext();T Current { get; }}

public interface IComparer<in T>{public int Compare(T left, T right);}

out關鍵字的意思是說IEnumerable<T>中T只會被用在輸出中,值不會被改變。這樣將IEnumerable<string>轉為IEnumerable<object>類型就是安全的。

in的意思正好相反,是說IComparer<T>中的T只會被用在輸入中,這樣就可以將IComparer<object>安全的轉為IComparer<string>類型。

前者被稱為Co-Variance, 后者就是Contra-Variance。

.Net4.0中使用out/in聲明的Interface:

System.Collections.Generic.IEnumerable<out T>System.Collections.Generic.IEnumerator<out T>System.Linq.IQueryable<out T>System.Collections.Generic.IComparer<in T>System.Collections.Generic.IEqualityComparer<in T>System.IComparable<in T>

Delegate:

System.Func<in T, …, out R>System.Action<in T, …>System.Predicate<in T>System.Comparison<in T>System.EventHandler<in T>

C# 5.0

異步文件 I/O

// Three things to note in the signature:
//  - The method has an async modifier. 
//  - The return type is Task or Task<T>. (See "Return Types" section.)
//    Here, it is Task<int> because the return statement returns an integer.
//  - The method name ends in "Async."
async Task<int> AccessTheWebAsync()
{ 
    // You need to add a reference to System.Net.Http to declare client.
    HttpClient client = new HttpClient();

    // GetStringAsync returns a Task<string>. That means that when you await the
    // task you'll get a string (urlContents).
    Task<string> getStringTask = client.GetStringAsync("http://msdn.microsoft.com");

    // You can do work here that doesn't rely on the string from GetStringAsync.
    DoIndependentWork();

    // The await operator suspends AccessTheWebAsync.
    //  - AccessTheWebAsync can't continue until getStringTask is complete.
    //  - Meanwhile, control returns to the caller of AccessTheWebAsync.
    //  - Control resumes here when getStringTask is complete. 
    //  - The await operator then retrieves the string result from getStringTask.
    string urlContents = await getStringTask;

    // The return statement specifies an integer result.
    // Any methods that are awaiting AccessTheWebAsync retrieve the length value.
    return urlContents.Length;
}

如果 AccessTheWebAsync 沒有它可以完成調用 GetStringAsync 和等待計算完成之間的任何工作,通過調用和等待簡化代碼在下面的單個語句。

 

string urlContents = await client.GetStringAsync();
  • 方法簽名包含一個 Async 或 async 修飾符。

  • 異步方法的名稱以“Async”后綴,按照約定,關閉。

  • 返回類型為下列類型之一:

    • Task<TResult> ,如果您的方法具有操作個線程類型 TResult 的返回語句。

    • Task ,如果方法沒有返回語句或具有返回語句不操作。

    • 無效 (在 Visual Basic 中 ),如果您編寫一個異步事件處理程序。

    有關更多信息,請參見“之后返回類型和參數”本主題。

     

  • 方法通常包含至少一個等待表達式,指示個方法無法繼續,直到該等待的異步操作完成的。 同時,方法被掛起,並且,控件返回到方法的調用方。 本主題的下一節將解釋發生的懸掛點。

在異步方法,可以使用提供的關鍵字和類型指示要執行,因此,編譯器執行方式,包括記錄必須出現,當控件處於掛起的方法時回時間點。 某些實例處理,例如循環,而異常處理,可能很難進行在傳統異步代碼的句柄。 在異步方法,解決您編寫這些元素,因為在一個同步解決方案會並將問題。

調用過程:

跟蹤異步程序

在關系圖的數值對應於以下步驟。

  1. <?XML:NAMESPACE PREFIX = [default] http://www.w3.org/1999/xhtml NS = "http://www.w3.org/1999/xhtml" />事件處理程序調用並等待 AccessTheWebAsync 異步方法。

  2. AccessTheWebAsync 創建 HttpClient 實例並調用 GetStringAsync 異步方法下載網站內容作為字符串。

  3. 掛起團隊進度的內容發生 GetStringAsync 發生。 可能必須等待網站下載或一些其他塊的事件。 若要避免妨礙資源,GetStringAsync 為控件對其調用方,AccessTheWebAsync。

    GetStringAsync 返回 TResult 是字符串的 Task<TResult>,並且,AccessTheWebAsync 將任務指派給 getStringTask 變量。 在工作完成時,任務表示繼續對定向到 GetStringAsync,以提交導致實際字符串值。

     

  4. 由於 getStringTask 不等待,AccessTheWebAsync 可以繼續執行不依賴於從 GetStringAsync的最終結果的其他工作。 該任務由為同步方法DoIndependentWork的調用表示。

  5. DoIndependentWork 完成其工作並回調用方的同步方法。

  6. AccessTheWebAsync 用完了它可以完成,不會受到 getStringTask的結果的工作。 接下來AccessTheWebAsync 若要計算並返回該下載的字符串的長度,但是,該方法無法計算該值,直到該方法具有字符串。

    因此,AccessTheWebAsync 使用一個等待運算符掛起的進度並使控件到調用 AccessTheWebAsync的方法。 AccessTheWebAsync 返回 Task(Of Integer) 或 Task<int> 調用方。 任務表示形式導致為下載的字符串的長度的整數結果。

    說明 說明

    如果 GetStringAsync (並 getStringTask) 后,在 AccessTheWebAsync 等待之前,控件在 AccessTheWebAsync保持。 成本掛起然后返回到AccessTheWebAsync 將浪費,如果異步調用過程 (getStringTask) 已經完成了,並且 AccessTheWebSync 不必等待最終結果。

    在調用方 (在此示例中的事件處理程序內),處理重復。 調用方可能完成不依賴於從 AccessTheWebAsync 的結果在等待該結果之前的其他工作,或調用方可能立即等待。 在事件處理程序到達等待表達式時,應用程序集中精力 GetStringAsync的完成。 事件處理程序等待 AccessTheWebAsync,並且,AccessTheWebAsync 等待 GetStringAsync。

     

  7. GetStringAsync 完成並生成一個字符串結果。 字符串結果不通過對 GetStringAsync 的調用返回方式與您可能期望的。 (請確保方法返回已在第 3 步 .) 的任務,字符串結果在表示方法的完成的任務,getStringTask存儲。 等待運算符從 getStringTask檢索結果。 賦值語句將檢索的結果賦給urlContents。

  8. 當 AccessTheWebAsync 具有字符串結果時,方法可以計算該字符串的長度。 然后 AccessTheWebAsync 工作也完成的,因此,等待事件處理程序可以繼續。 在完整的示例中的主題末尾,可以確認事件處理程序檢索和打印長度結果的值。

如果您不熟悉異步編程,請需要一分鍾考慮同步和異步行為之間的差異。 一個同步方法返回,其工作完成 (第 5 步),但是,異步方法返回任務值,其工作掛起時 (第 3 步和第 6 步)。 在異步方法最終完成其工作時,任務將會標記為已完成和結果,如果有,在任務中。


API 異步方法


在什么情況下可能想知道找到支持異步編程的方法 (如 GetStringAsync。 .NET Framework 4.5 包含與異步以及等待的許多成員。 您可以通過附加到成員名稱和 TaskTask<TResult>的返回類型“Async”后綴識別這些成員。 例如,System.IO.Stream 選件類包含方法例如 CopyToAsyncReadAsyncWriteAsync 在同步方法 CopyToReadWrite

線程


異步方法旨在成為非阻塞操作。 當等待的任務運行時,在異步方法的一個等待表達式不會阻止當前線程。 相反,該表達式注冊該方法的其余部分作為繼續並返回控制對異步方法的調用方。

異步和等待關鍵字不會導致其他線程創建。 因為異步方法本身並不會運行的線程,異步方法不需要多線程。 只有 + 當方法處於活動狀態,則方法在當前同步上下文中運行並使用在線程的時間。 可以使用 Task.Run 移動 CPU 工作移到后台線程,但是,后台線程不利於等待結果變得可用處理。

以異步編程的基於異步的方法優於於幾乎每個用例的現有方法。 具體而言,此方法比 IO 操作的 BackgroundWorker 好,因為代碼更為簡單的,因此無需防止爭用條件。 與 Task.Run的組合,異步編程的 CPU 操作的 BackgroundWorker 好,因為異步編程從 Task.Run 傳輸到線程池的工作分隔運行您的代碼以協調詳細信息。

異步和等待


如果指定使用 異步異步 修飾符,方法是異步方法,可以實現以下兩個函數。

  • 清單異步方法可以使用 Await 或指定的 等待 懸掛點。 等待運算符通知編譯器異步方法不能繼續點的過去,直到等待的異步過程完成。 同時,控制權交還異步方法的調用方。

    一個異步方法的備用在等待表達式的不構成從方法的退出,並且,finally 塊不會運行。

     

  • 清單異步方法本身可以通過調用它的方法等待。

異步方法通常包含等待運算符的一個或多個匹配項,但是,請假等待表達式不會導致編譯器錯誤。 如果異步方法不會將等待運算符指示懸掛點,方法盡管"修飾符執行,一個同步方法。 編譯器會發出此類方法的警告。

Async 、async、Await和 await 是上下文關鍵字。 有關更多信息和示例,請參見以下主題:

返回類型和參數


在編程 .NET framework,異步方法通常返回 TaskTask<TResult>。 在異步方法中,等待運算符應用於從調用返回到另一個異步方法的任務。

您指定 Task<TResult>,因為返回類型,則方法包含指定類型 TResult操作上的一個 返回 (Visual Basic) 或 返回 (c#) 語句。

使用 Task,因為返回類型,則該方法沒有返回語句或具有不返回操作線程的 return 語句。

下面的示例演示如何聲明並調用返回 Task<TResult>Task的方法。

C#

VB

// Signature specifies Task<TResult>
async Task<int> TaskOfTResult_MethodAsync()
{
    int hours;
    // . . .
    // Return statement specifies an integer result.
    return hours;
}

// Calls to TaskOfTResult_MethodAsync
Task<int> returnedTaskTResult = TaskOfTResult_MethodAsync();
int intResult = await returnedTaskTResult;
// or, in a single statement
int intResult = await TaskOfTResult_MethodAsync();


// Signature specifies Task
async Task Task_MethodAsync()
{
    // . . .
    // The method has no return statement.  
}

// Calls to Task_MethodAsync
Task returnedTask = Task_MethodAsync();
await returnedTask;
// or, in a single statement
await Task_MethodAsync();

每個返回的任務表示正在進行的工作。 任務封裝有關狀態的信息異步過程和,最后,從進程的最終結果或處理引發的異常;如果未成功。

異步方法也是 Sub 方法 (Visual Basic) 或使 void 返回類型 (c# 中)。 這將返回類型主要用於定義事件處理程序,void 返回類型需要。 異步事件處理程序通常用作異步程序的起始點。

是 Sub 程序或具有 void 返回類型不能等待的異步方法和一個無效返回的方法的調用方無法捕獲方法引發的任何異常。

異步方法不能聲明在 Visual Basic 或 refByRef 參數或在 C# 中 http://msdn.microsoft.com/zh-cn/library/t3c3bfhx.aspx 參數,但是,方法可以調用具有這些參數的方法。

有關更多信息和示例,請參見異步返回類型(C# 和 Visual Basic)。 有關如何捕獲異步方法的異常的更多信息,請參見 try-catch(C# 參考)Try...Catch...Finally 語句 (Visual Basic)

在 Windows 運行時 編程的異步 API 使之一返回類型,類似於任務:

有關更多信息和示例,請參見 快速入門:使用異步編程的時間運算符

命名約定


按照約定,您追加“Async”傳遞給具有 Async 或 async 修飾符方法的名稱。

您可以忽略事件、基類或接口協定建議一個不同的名稱約定。 例如,您不應向常用事件處理程序重命名,例如 Button1_Click。

 

弱事件

WeakEventManager<TEventSourceTEventArgs>


免責聲明!

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



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