程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> .NET網頁編程 >> C# >> C#入門知識 >> C#高效編程話題集3

C#高效編程話題集3

編輯:C#入門知識

不如來編碼-luminjis web

本期話題:

1:使用屬性還是字段

 首先重大區別就是屬性實質是方法,所以:
1:可以為屬性添加代碼;
2:可以讓屬性支持線程安全;見effective c#第一版的第一章;
3:屬性得到了VS編輯器的支持,得以實現自動屬性這種功能。
4:自動屬性的特點在LINQ中得到了廣泛應用,尤其是匿名類型中,只能實現只讀的自動屬性,匿名類型不支持字段;
5:從設計的角度,也就是面向對象的角度,建議使用屬性;
6:如果某個屬性僅僅作為類型內部使用,而且不涉及到上面5點內容,則建議使用字段

還有一點,屬性在序列化中有點尴尬,由於屬性是方法,所以不能為其指定[NonSerialized]特性。

2:利用泛型拓寬方法的應用范圍

假設存在方法:

static void PrintSalary(ISalary<Employee> s){    s.Pay();}
然後像下面這樣使用它:

ISalary<Programmer> s = new BaseSalaryCounter<Programmer>();PrintSalary(s);
注意,幾個主要類的代碼:

interface ISalary<T>{    void Pay();}class BaseSalaryCounter<T> : ISalary<T>{    public void Pay()    {        Console.WriteLine("Pay base salary");    }}class Employee{    public string Name { get; set; }}class Programmer : Employee{}
運行結果是:

無法從“ConsoleApplication4.ISalary<ConsoleApplication4.Programmer>”轉換為“ConsoleApplication4.ISalary<ConsoleApplication4.Employee>”

顯然我們認為PrintSalary方法因為能支持ISalary<Employee> ,所以肯定能支持ISalary<Programmer>,這是一種很容易犯的嘗試錯誤。

改進方法先提供一種思路,就是將該方法改成泛型。

3:利用協變關鍵字out

 要解決話題2中的問題,還可以使用out關鍵字。即修改接口為:

interface ISalary<out T>{    void Pay();}

4:減少使用類型的靜態變量

1:靜態變量一旦被創建不被釋放;

2:靜態變量不是線程安全的,它不像類型變量只對創建類型的那個線程有效;

5:線程同步有多少種方法?

 所謂線程同步,采用的技術,就是在某個對象上等待(也理解為鎖定該對象)。C#中類型對象的分類:引用類型和值類型。在這兩種類型上的等待是不一樣的,我們也可以簡單的理解為在CLR中,值類型是不能被鎖定的(參考MSDN的volatile關鍵字)。

在引用類型上的等待,又分為兩種技術:鎖定和信號機制。

鎖定使用:關鍵字lock和類型Monitor,前者其實是後者的語法糖。這是最常用的同步技術,小伙們應該都用過;

信號機制:信號機制中涉及的類型都繼承自抽象類WaitHandle,這些類型有EventWaitHandle(類型化為AutoResetEvent、ManualResetEvent)和Semaphore以及Mutex。它們之間有一定的區別,值得一提的是Mutex能同步不同應用程序域。其它幾個類型的不同點MSDN寫的很傷人,而且未進行橫向比較,簡單來說,就是EventWaitHandle通過布爾型進行同步,其中AutoResetEvent又自動恢復類型的阻滯狀態,ManualResetEvent則必須手動恢復阻滯狀態。Semaphore通過整型進行同步。

6:讓對象=null能否加速對象被垃圾回收器回收?

 注意,以下描述都只針對引用類型

1:在方法內,方法參數和局部變量,賦值為null無助於加速被垃圾回收器標識為垃圾;

2:實例變量,隨著實例=null,實例變量也會自動=null。所以,一般情況下,無需顯式為實例變量=null,除非你的實例生存周期較長,並且實例變量是個大對象;

3:靜態變量,如果不顯式置為null,就永遠不會被回收;

7:不建議lock(this)

 1:如果兩個對象的實例分別執行了lock(this)這樣的代碼,實際鎖定的是兩個對象,完全不能達到同步的目的。

2:最好避免鎖定 public 類型或鎖定不受應用程序控制的對象實例。例如,如果該實例可以被公開訪問,則 lock(this) 可能會有問題,因為不受控制的代碼也可能會鎖定該對象。這可能導致死鎖,即兩個或更多個線程等待釋放同一對象。出於同樣的原因,鎖定公共數據類型(相比於對象)也可能導致問題。

8:區分計算密集型和I/O密集型的多線程應用場景

I/O密集型操作。硬盤、網卡、聲卡、顯卡等都是。CLR所提供的異步編程模型就是讓我們充分利用硬件的DMA功能來提高CPU的利用率。

一個在大多數情況下正確的技巧是,凡是FCL中類型提供了類似BeginDoSomething方法的,都建議使用這個異步調用來完成多線程編碼,異步在後台調用線程池線程完成調度,最大化的節約了系統的性能。

9:使用Parallel的一個陷阱

 以下的代碼的輸出是什麼?

int[] nums = new int[] { 1, 2, 3, 4 };long total = 0;Parallel.For<long>(0, nums.Length, () =>{    return 1;}, (i, loopState, subtotal) =>{    subtotal += nums[i];    return subtotal;},    (x) => Interlocked.Add(ref total, x));Console.WriteLine("The total is {0}", total);
實際上,它有可能是11,較少的情況下會是12,幾乎不可能出現13,14。

要從方法的最後一個參數Action<TLocal> localFinally講起,它表示的其實是並行中如果新起了一個線程,那麼這個線程結束時會調用這個localFinally。 而我們知道並行在後台采用的是線程池,也就是,我們不知道上面代碼所發起的這個並行實際會發起多少個線程,如果是1個,自然就是11,如果是2,結果自然就是12了,依此類推。之所以不可能為14,是因為,並發的循環體就只有4個循環,並發顯然不會傻到新起4個線程來執行並發。

10:不建議lock(類型的type)

 由於typeof(SampleClass),是SampleClass的所有實例所共有的,這會導致當前應用程序中的所有SampleClass的實例的線程,將會全部被同步。

 

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved