C#編程語言及.NET 平台快速入門指南


e-mail: hamlet.jiang@live.com
 

⼀、C#,CLR,IL,JIT概念 以及 .NET 家族

(⼀)基礎概念

C# (念作 C Sharp) 是在CLR上實現的一種編程語言,也是.NET平台上最通用的編程語言,它在語法上借鑒了Java和C++風格,但更為精簡。Borland Turbo Pascal編譯器的主要作者安德斯·海爾斯伯格(Anders Hejlsberg)是C#與.NET平台的創始人。本文詣在為初次接觸C#和.NET平台的用戶提供較全面的路線指引,也為早期.NET開發人員介紹當代.NET平台的新特性。
 
相對於 C 和 C++,C# 在許多方面進行了限制和增強:
1、指針(Pointer)只能用於不安全模式之中。大多數對象訪問通過安全的引用實現,以避免無效的調用,並且有許多算法用於驗證溢出,指針只能用於調用值類型,以及受垃圾收集控制的托管對象。
2、對象不能被顯式釋放,代替為當不存在被引用時通過垃圾回收器回收。
3、只允許單一繼承(single inheritance),但是一個類可以實現多個接口(interfaces)。
4、C# 比 C++ 更加類型安全。默認的安全轉換是隱含轉換,例如由短整型轉換為長整型和從派生類轉換為基類。而接口布爾型同整型,及枚舉型同整型不允許隱含轉換,非空指針(通過引用相似對象)同用戶定義類型的隱含轉換字段被顯式的確定,不同於C++的復制構造函數。
5、數組聲明語法不同("int[] a = new int[5]"而不是"int a[5]")。
6、枚舉位於其所在的名字空間中。
7、C# 中沒有模版(Template),但是在C# 2.0中引入了泛型(Generic programming),並且支持一些 C++ 模版不支持的特性。比如泛型參數中的類型約束。另一方面,表達式不能像C++模版中被用於類型參數。
8、屬性支持,使用類似訪問成員的方式調用。
9、完整的反射支持。
 
 CLR-Common Language Runtime 意為公共語⾔運⾏庫,它是⼀個可由多種不同編程語⾔使⽤的運⾏庫,只要是⾯向 CLR 的編譯器編譯的編程語⾔都被 CLR ⽀持。
 
 IL-Intermediate Language,意為中間語⾔,⾯向 CLR 的編程語⾔被編譯為IL代碼,IL代碼也被稱為托管代碼,它是與 CPU ⽆關的機器語⾔,是⼀種⾯向對象的機器語⾔。每⼀個 IL 代碼⽂件被稱為托管模塊(managed module)。托管模塊是 ⼀個32位或是64位可移植執⾏體⽂件,它們需要CLR才能執⾏。
 
 每個托管模塊帶有相應的元數據(metadata),元數據描述模塊中定義的內容,⽐如類型及成員、導⼊的類型及成員。每 個托管模塊由操作系統頭信息、CLR頭(記錄版本、⼊口⽅法等)、元數據、IL代碼(CLR在運⾏時將IL編譯成本地CPU 指令)。 ⼀個.NET程序集是由⼀個或者多個托管模塊和資源⽂件組成,程序集是⼀個或是多個托管模塊的邏輯分組,是最⼩的可重用、安全性及版本控制單元。
 
 JIT-just-in-time,意為CLR對IL代碼進⾏即時編譯的過程,CLR擁有進⾏JIT過程的編譯器(JITComiler),它將要調⽤的 IL 代碼編譯為本地 CPU 指令。
 
.NET CORE 即將像C++一樣支持 Intel CPU SIMD 指令集(從SSE到AVX2),參考下列資料:
 

(二).NET 家族

本文將Windows上的.NET Framework稱為經典 .NET,由公共語⾔運⾏庫(CLR)和類庫(FCL --Framework Class Library)構成。
.NET Core 是 經典.NET 的跨平台實現,.NET Standard是 .NET Core 和 .NET Framework之間的通用庫。
Mono是一個由Xamarin公司所主持的開源項目。該項目的目標是創建一系列匹配ECMA標准的.NET工具,包括C#編譯器和通用語言架構。
ML.Net 是.NET Core上實現的AI開發框架。
 
開發Windows應用建議選擇經典.NET (v.4.x)或 .NET Core 3.0+ (Winfrom和WPF);
開發Linux上的微服務、Web服務、docker容器服務建議使用.NET Core (v.2.2.x、v.3.0.x) ;
開發跨平台手機應用建議使用.NET Xamarin框架(支持ios,Aandroid);
 
.NET Core 3.0 正式公布:新特性詳細解讀
 
注:Visual Studio 2019 支持用戶使用以上任何一個框架開發應用,並內置相關應用場景的項目模板。
Visual Studio 2017支持.NET v.4.x 及.NET Core 2.2的項目開發。
 
微軟公司在2014年開源了Roslyn編譯器,隨后成立了.NET 開源基金會,並在 Github上以MIT協議公開了.NET源代碼。詳情參考: https://github.com/dotnet
 
.NET 5 將在2020年推出,它將統一目前所有的 .NET 分支。

上圖為.NET 5 架構圖

上圖是 .NET 發布路線圖

 

一家名為iolevel的公司推出了peachpie編譯器(https://www.peachpie.io/)致力於將PHP語言帶到.NET平台。他們之前是一所大學團體(位於布拉格的查爾斯大學),推出過名為Phalanger的編譯器( https://github.com/DEVSENSE/Phalanger)將一部分Facebook的開源代碼轉換為.NET代碼來執行。這些都可以證明.NET平台的先進性。點擊這里了解一些peachpie的背景:https://kb.cnblogs.com/page/138573/

二、C# 語言要點

(一)基元類型

C#類型 FCL 類型 說明
Sbyte System.Sbyte 有符號8位值 (⼀位即1bit,8bit即1byte,下同)
byte System.Byte ⽆符號8位值
Short System.Int16 有符號16位值
ushort System.UInt16 無符號16位值
int System.Int32 有符號32位值
uint System.UInt32 無符號32位值
Long System.Int64 有符號64位值
ULong System.UInt64 無符號64位值
char System.Char 16位Unicode字符
Float System.Single IEEE32位浮點
Double System.Double IEEE64位浮點
Bool System.Boolean ⼀個true/false值
Decimal System.Decimal 128位⾼精度浮點值
String System.String ⼀個字符數組
Object System.Object 所有類型的基類型
 

(二)引用類型和值類型

CLR⽀持引⽤類型和值類型。 引⽤類型總是從托管堆上分配,C#的new操作符會返回對象的內存地址。結構與枚舉都是值類型,與引⽤類型相⽐,值類型是⼀種輕量級的類型,值類型實例是在線程的堆棧上分配,值類型不需要內存指針,不需要垃圾收集處理。所有類型 都是System.Object派⽣,所有值類型都是由System.ValueType抽象類派⽣。
 

(三) 值類型的裝箱與拆箱:

當需要⼀個值類型進⾏實例引⽤時產⽣裝箱(boxing) ,裝箱過程是從托管堆中分配內存,並將值類型字段復制到新分 配的堆內存,然后返回新對象的引⽤。 
裝箱情景:
Struct Point{public int32 x,y;}
 
 Public sealed class Program
 { 
    Public static void Main()
     {
         ArrayList a = new ArrayList();
         Point p; 
         For(int32 i =0;i<10;i++) 
         {
             p.x = p.y = i;
             a.Add(p);//這⾥產⽣裝箱,Add⽅法⼊參必須是Object類型,⽽Object類型是⼀個引⽤類型,值類型P要被裝箱為引⽤ 類型。
         }         
     } 
}
 
上例中,ArrayList內的p元素是引⽤類型,與原 Point P 結構脫離了關系。
 
 拆箱情景(unboxing): 
Point p2 =(Point)a[0];//這⾥產⽣拆箱
 
拆箱是獲取已裝箱對象各個字段的地址(拆箱關鍵),並將已經裝箱的對象的字段值復制到新的值類型變量的字段。拆 箱時只能將對象拆箱為它裝箱時的類型。
 
⼿動控制裝箱的速度將⽐編譯器裝箱的速度快。 
如:
 1)Int32 v =5;Console.writeLine(“{0}{1}{2}”,v,v,v);
 2)Int32 v=5;object o =v(⼿動裝箱);Console.writeLine(“{0}{1}{2}”,o,o,o)//這個⽅法快

(四)類型、類成員、接口

類型基礎
類型:是可以在類型內部嵌套地定義其他類型的邏輯單位。
類型的成員種類:常量、字段、實例構造器、類型構造器(靜態構造)、⽅法、操作符重載、轉換操作符、屬性、靜態 事件、實例事件。
訪問修飾符表:
名稱 用作類 用作類成員

Public

該類型對所有程序集是可見的 成員可以由所有程序集的所有⽅法訪問
Protected internal   成員可以由所在類型及其嵌套類型、所有派⽣類型 (不限程序集)、類型所在程序集的所有⽅法訪 問。
Internal 該類型僅在程序集內部可見以及友元程序 集可見 成員可由當前程序集中的所有⽅法訪問
Protected   成員只能由定義該成員類型中的⽅法、該類型的所 有嵌套類型的⽅法、或者該類型的⼀個派⽣類型 (不限程序集)的⽅法訪問
Private   成員只能由定義該成員的類型中的⽅法或者該類型 的所有嵌套類型中的⽅法訪問

 

組件版本控制修飾符表:

名稱 用作類 用作方法、屬性、事件 ⽤作常量/字段
Abstract 表⽰該類型不能構建實例 表⽰在構建派⽣類的實例之前派⽣類 型必須實現這個成員  
Virtual   表⽰這個成員可以由派⽣類型重寫  
Override   表⽰派⽣類型重寫了基類型的成員  
Sealed 表⽰該類型不能⽤作基類 表⽰該成員不能被派⽣類型重寫  
new 應⽤於嵌套類型、⽅法、屬性、事件、常量或字段時,表⽰該成員與基類中類似的成員沒有關系  

 

靜態類(static class):靜態類是不需要實例化,僅擁有靜態成員的類型。靜態類不⽀持接⼜,這是因為只有使⽤類的實 例的時候才調⽤類的接⼜⽅法。靜態類型只包括靜態成員,靜態類本⾝不能⽤作字段、⽅法參數或者局部變量。
 
部分類(partial class):為了將⼀個類分布在多個⽂件中編輯⽽采⽤partial修飾符,它們在編譯后成為⼀個類。
 
索引器(indexer):索引器是⼀種參數化的成員屬性。索引器不⽀持靜態類型。索引器的作⽤是為類型向外界間接提供 內部的集合成員。
例:
public object this[int x]{get;set;},public object this[int x,int y]{get;set;}
 
可變參數⽅法:以params關鍵字修飾的參數稱為可變參數,它允許輸⼊數量不定的參數來調⽤⽅法。
例:
 Public static double GetAvg(params double[] list){…}; GetAvg(1,2,12,4,3.2);GetAvg(1,57.3);
 
基類初始化(initializer)調⽤:⼦類在實例化時可以⼀並調⽤基類的構造函數。這在多個類共享基類構造函數設置的⼀ 些公共成員屬性時更便利。
例:
Public class ClassA
{
    public ClassA(int a,string b){…}
}
 
Public class ClassB:ClassA
{
    public ClassB(int a,string b,bool c):base(a,b){…}
}
 
類型的私有構造函數常被⽤於只通過靜態⽅法和字段來提供功能的類型。采⽤私有構造函數的類不能被外部類實例化, 但可以在內部實例化。
 
 靜態構造函數⽤於初始化靜態成員,也只能訪問靜態成員,不管類型被實例化多少次,靜態構造函數只執⾏⼀次。
 
C# 特性標記的使用
[AttributeUsage(AttributeTargets.Property | AttributeTargets.Class, Inherited = true)]
public class CustomerAttribute:Attribute
{
      public String Name{get;set;}   
}
 
使用反射獲取特性標記值
[CustomerAttribute(){Name="Sample"}]
public class Sample(){}
Sample o = new Sample();
Type ot = typeof(o);//typeof(t)
ot.GetCustomAttributes();
 
C# 匿名擴展方法
public class A
{
      public A();
     public void M1();
}
 
public static class M
{
      public static M2(this A a )
      {
            //do sth.
      }
}
 

(五) 集合類型 Array\ArrayList\List\HashTable(哈希表)\Dictionary(字典)\Stack(堆棧)\Queue(隊列)

Array類型:是實現數組的基類,只有系統和編譯器可以派⽣。Array提供CreateInstance⽅法進⾏后期綁定,沒有公共構 造函數。以下都是聲明數組的⽅式:
Array my1DArray=Array.CreateInstance( typeof(Int32), 5 ); 
Int32[] my2DArray = new Int32[5]{1,2,3,4,5}
 
ArrayList類型:是⼤⼩按需⾃動增加的Array實現,實現了IList接口。以下是ArrayList常見⽤法:
ArrayList myAL = new ArrayList(); 
myAL.Add("Hello");
myAL.Add("World"); myAL.Add("!");
 
HashTable: 表⽰鍵/值對的集合,這些鍵/值對根據鍵的哈希代碼進⾏組織。
例:
public static void Main() {
// Creates and initializes a new Hashtable.
Hashtable myHT = new Hashtable();
myHT.Add("First", "Hello");
myHT.Add("Second", "World");
myHT.Add("Third", "!");
// Displays the properties and values of the Hashtable.
Console.WriteLine( "myHT" );
Console.WriteLine( " Count: {0}", myHT.Count );
Console.WriteLine( " Keys and Values:" );
PrintKeysAndValues( myHT );
}
public static void PrintKeysAndValues( Hashtable myHT ) {
Console.WriteLine( "\t-KEY-\t-VALUE-" );
foreach ( DictionaryEntry de in myHT )
Console.WriteLine("\t{0}:\t{1}", de.Key, de.Value);
Console.WriteLine();
}
 
Dictionary:是HashTable的泛型實現
 
Stack:表⽰對象的簡單的后進先出⾮泛型集合。Stack 的容量是 Stack 可以保存的元素數。Stack 的默認初始容量為 10。 向 Stack 添加元素時,將通過重新分配來根據需要⾃動增⼤容量。Stack常被當作循環緩沖區。
 
Queue(隊列):是表⽰對象的先進先出集合,與Stack相反。隊列在按接收順序存儲消息⽅⾯⾮常有⽤,以便於進⾏順 序處理。此類將隊列作為循環數組實現。存儲在 Queue 中的對象在⼀端插⼊,從另⼀端移除。
Queue 的容量是 Queue 可以保存的元素數。Queue 的默認初始容量為 32。向 Queue 添加元素時,將通過重新分配來根據 需要⾃動增⼤容量。
 

(六)泛型

泛型(generic)是CLR與編程語⾔提供的⼀種實現“算法重⽤”的機制。
例:
 List sl = new List();sl.add(DateTime.Now);sl.add(DateTime.MinValue);
 
泛型對象設計⽤於管理在類型上成家族的集合,例如設計⼀個⼯⼚類型⽤於創建或修改基於某個接口演變的多個⼦類型 的對象。
 
例:
/// <summary>
/// 為安全成員對象提供公共服務
/// </summary>
public abstract class SecurityMemberService<T> where T:ISecurityMember
{
    public abstract T MemberLogin(string memberUserName,string memberPassword);
    public abstract T MemberLogin(string memberEmail,string memberPassword);
    public abstract bool MemberLogout(T member);
    public abstract T CreateMember(T obj,SecurityMemberInfo info);
    public abstract bool DeleteMember(T member);
    public abstract T FindMemberByUserName(string userName);
    public abstract T FindMemberByEmail(string email);
    public abstract bool LockMember(T member);
    public abstract bool UnlockMember(T member);
    public abstract bool ChangePassword(string memberName, string oldPassword,string newPassword);
    public abstract bool ChangePasswordQuestionAndAnswer(T member);
    public abstract bool ResetPasswordAndUpdate(T member);
}
 
在上例中,SecurityMemberService類型封裝了⼀般對ISecurityMember類型的處理⽅法,類型參數T可以是任意 實現了ISecurityMember接⼝的類型,這樣對這些類型的⼀般處理並不需要創建額外對應的⼯⼚類型。 注意:泛型類SecurityMemberService有⼀個對類型參數T的約束,它由where關鍵字指定。
 
在⾮泛型類中也可以有泛型⽅法成員,同樣泛型⽅法也可有類型約束。
例:
Public class A
{
Void M1<T>(T obj){obj.ToString();}
Void M2<T>(T obj)where T:ClassB {obj.ToString();}
}
 
委托也可以被設計成泛型,因為委托也可以被當作⽅法的⼀種定義形式,即委托本身描述的是回調⽅法的定義。
例:
Delegate void EventHandler(Object sender,TEventArgs e)where TEventArgs:EventArgs;
上例定義的EventHandler要求回調⽅法中的參數e必須是EventArgs類型或是EventArgs的派⽣類型,TEventArgs 是⼀個類型參數,相當於常⻅的T。

(七)線程 (Threading、Lock、Monitor、Mutex)

線程概述:
線程分為前台線程和后台線程,后台線程不妨礙程序的終⽌。線程具有優先級,優先級⾼的線程會得到更多的CPU時 間。多線程可以提⾼對CPU時間的利⽤率,但會占⽤更多的內存等資源。
 
線程安全:
Lock關鍵字可以將⼀段代碼定義為互斥段。互斥段在⼀個時刻內只允許⼀個線程進⼊執⾏,⽽其他線程必須等待。如果 有⼀些任務每次只能交給⼀個線程去操作,就可以使⽤Lock關鍵字將代碼定義為互斥段。
例:
Lock(this) 
{
     //do anything 
}
 
Monitor 類通過向單個線程授予對象鎖來控制對對象的訪問。對象鎖提供限制訪問代碼塊(通常稱為臨界區)的能⼒。當 ⼀個線程擁有對象的鎖時,其他任何線程都不能獲取該鎖。還可以使⽤ Monitor 來確保不會允許其他任何線程訪問正在由 鎖的所有者執⾏的應⽤程序代碼節,除⾮另⼀個線程正在使⽤其他的鎖定對象執⾏該代碼。
 
例:
Queue myQueue = new Queue();
Monitor.Enter(myQueue);
//可以在當前線程下對myQueue做任何操作。
Monitor.Exit(myQueue)//釋放鎖
 
為了保證在異常情況下仍可釋放鎖,Monitor.Exit()⽅法可以放在finally塊⾥。調⽤Monitor.Pulse()⽅法會通知預備隊列中的 線程可以⽴即使⽤釋放的對象。
 
Mutex類是同步基元。當兩個或更多線程需要同時訪問⼀個共享資源時,系統需要使⽤同步機制來確保⼀次只有⼀個線程 使⽤該資源。
Mutex只向⼀個線程授予對共享資源的獨占訪問權。如果⼀個線程獲取了互斥體,則要獲取該互斥體的第⼆個線程將被掛 起,直到第⼀個線程釋放該互斥體。已命名的系統互斥體(Mutex)在整個操作系統中都可見,可⽤於同步進程活動。
 
與Monitor類不同,Mutex可與WaitHandle⼀起構成“等待機制”,Mutex還可以穿越應⽤程序域。
例:
class Test
{
    // Create a new Mutex. The creating thread does not own the
    // Mutex.
    private static Mutex mut = new Mutex();
    private const int numIterations = 1;
    private const int numThreads = 3;
    static void Main()
    {
        // Create the threads that will use the protected resource.
        for(int i = 0; i < numThreads; i++)
        {
            Thread myThread = new Thread(new ThreadStart(MyThreadProc));
            myThread.Name = String.Format("Thread{0}", i + 1);
            myThread.Start();
        }
        // The main thread exits, but the application continues to
        // run until all foreground threads have exited.
    }
    private static void MyThreadProc()
    {
        for(int i = 0; i < numIterations; i++)
        {
            UseResource();
        }
    }
    // This method represents a resource that must be synchronized
    // so that only one thread at a time can enter.
    private static void UseResource()
    {
        // Wait until it is safe to enter.
        mut.WaitOne();
        Console.WriteLine("{0} has entered the protected area",
        Thread.CurrentThread.Name);
        // Place code to access non-reentrant resources here.
        // Simulate some work.
        Thread.Sleep(500);
        Console.WriteLine("{0} is leaving the protected area\r\n",
        Thread.CurrentThread.Name);
 
        // Release the Mutex.
        mut.ReleaseMutex();
    }
}
 

(八) C# 面向對象編程、繼承、多態、接口、委托、事件

基本概念
⾯向對象編程(Object –Oriented Programming,OOP),抽象、繼承和多態是OOP編程語⾔的三⼤要素。
 
繼承:類繼承的重要特性是,在希望出現基類型實例的任何地⽅,都可以替換成派⽣類型的實例。類似地,接口繼承允許在希望出現已命名接口類型的實例的任何地⽅,都可以替換成實現接口的⼀個類型的實現。
 
多態:指的是多個類型的對象對同⼀消息做出各⾃的處理。多態是⼦類對⽗類的⽅法進⾏重寫或替換⽽實現的。 
 
接口:接口是⼀組已命名的⽅法簽名,在接口內還可以定義事件和屬性,它們在本質上也是⽅法。C# 要求接口⽅法標記為 Public。接口的關鍵價值在於隱藏類型的設計細節,即外部對象不依賴當前對象的內部細節。
 
接口特性
接口⽅法的隱式實現:當⽅法簽名與繼承的接口中的簽名⼀致,並且是public或者是viture修飾的⽅法都視為隱式實現了接口⽅法。
例:
Internal sealed class SimpleType:IDisposable
{
Public void Dispose(){Console.WriteLine(“Dispose”);}
}
 
接口⽅法的顯式實現:以接口類型名稱作為⽅法前綴時,創建的是⼀個顯式接口⽅法實現(explicit interface method implementation,EIMI)。⼀個EIMI⽅法不允許標記訪問性(⽐如公共或私有),也不能被標記為virture,因⽽也不能被重 寫。顯⽰接口⽅法會損害性能,應當謹慎使⽤。
例:
Internal sealed class SimpleType:IDisposable
{
Public void Dispose(){….}
Void IDisposable.Dispose(){….}//顯式
}
 
對顯式接口的調⽤,需要通過⼀個接口類型的變量來進⾏。
例:
SimpleType st = new SimpleType();
IDisposable d = st;
d.Dispose();
 
泛型接口有如下優點:
1、使用接口方法變為強類型。
2、泛型接口在操作值類型時,會減少裝箱操作。
3、類可以實現同一個接口若干次,只要使用不同的類型參數。
例:
Public sealed class Number:IComparable<Int32>,IComparable<String>
{
Private int32 m_val =5;
 
//實現IComparable<Int32>
Public Int32 CompareTo(Int32 n){return m_val.CompareTo(n);}
 
//實現IComparable<String>
Public Int32 CompareTo(String s){return m_val.CompareTo(Int32.Parse(s));}
}
 
委托
委托是.NET中的回調機制。將一個方法綁定到一個委托時,C#和CLR允許引用類型的協變(covariance)和反協變(contra-variance)。協變是指一個方法能返回一個從委托的返回類型派生出來的類型。反協變是指一個方法的參數類型可以是委托的參數類型的基類。但協變對於返回值類型或void的方法不適用。
例:
//MyCallback委托
Delegate object MyCallback(FileStream s);
 //SomeMethod⽅法 
String SomeMethod(Stream s);
 
上例中,SomeMethod的返回類型(String)繼承⾃委托返回類型(Object),這種協變是允許的。SomeMethod的參數類型
(Stream)是委托的參數類型(FileStream)的基類。這種反協變是允許的。
 
鏈式委托指的是⽤⼀個委托回調多個⽅法,即⼀系列委托對象組成的集合。Delegate的公共靜態⽅法Combine⽤於添加⼀ 個委托到委托鏈,Remove⽅法⽤於從鏈中刪除⼀個委托對象。在C#中內置的+=與-=操作符簡化了這些操作。
例:
Internal delegate void Feedback(int32 value);
Feedback fb1 = new Feedback(….);
Feedback fb2 = new Feedback(….);
fbChain =(Feedback)Delegate.Combine(fbChain,fb1);
fbChain =(Feedback)Delegate.Combine(fbChain,fb2);
 
⼀組委托是按順序執⾏的,如果他們帶有返回值,只能得到最后⼀個委托的返回值,如果其間有委托⽅法出現致命錯誤,其它委托就⽆法執⾏。為了克服這些問題,產⽣了MulticastDelegate類,它的GetInvocationList⽅法⽤於顯式調⽤鏈中的每 ⼀個委托,並使⽤符合⾃⼰需求的任何算法。MulticastDelegate類是特殊的類型,只能由系統派⽣,Delegate類已經具備了 MulticastDelegate的能⼒。
 
委托的便捷實現:
1. 不構造委托對象
例:
 internal sealed class AClass 
     public static void CallbackWithoutNewingADelegateObject()  
    { 
        ThreadPool.QueueUserWorkItem(SomeAsyncTask,5); 
    } 
   
   private static void SomeAsyncTask(Object o)
   {
       Console.WriteLine(o);
   } 
}
 
上例中ThreadPool類的靜態⽅法QueueUserWorkItem期望接收⼀個WaitCallback委托對象引⽤,該對象又包含⼀個 SomeAsyncTask⽅法引⽤。因為C#編譯器能夠⾃⼰進⾏推斷,所以我們可以省略構造WaitCallback對象的代碼。
 
2. 不定義回調⽅法
例: 
internal sealed class AClass 
     public static void CallbackWithoutNewingADelegateObject() 
     {  
        ThreadPool.QueueUserWorkItem(delegate(Object obj){Console.WriteLine(obj);},5)  
     } 
上例中⽤了⼀段代碼塊替代了回調⽅法名,編譯器會⾃動在類中增加⼀個經過命名的基於此代碼塊的回調⽅法。
 
3. 不指定回調⽅法的參數
例:
button1.Click += delegate(Object sender,EventArgs e){MessageBox.Show(“The Button was clicked”);} 
//由於上述⽅法中沒有⽤到sender與e兩個參數,可簡寫為:
button1.Click+=delegate{MessageBox.Show(“ The Button was clicked”);}
 
4. 不需要將局部變量⼈⼯封裝到類中,即可傳給⼀個回調⽅法
 
事件
事件:在.NET中事件(event)是類的成員,與成員屬性和⽅法⼀樣。類型的事件,是對外提供的⾃⾝狀態的通知。外部類 型通過訂閱的形式與事件的發布類型進⾏協作。將事件與處理⽅法關聯起來的是委托。.NET中⽤event關鍵指定特定的委托 來為事件做出響應,這樣做可以限制其它⽅法對委托的調⽤(在內部定義委托為私有的,通過event公開,因此外部⽆法訪 問委托中的⽅法)。
 
設計線程安全的事件,必須顯⽰地控制事件的訂閱與注銷。
例:
internal class MailManager
{
    //創建⼀個作為線程同步鎖的私有實例字段
    private readonly Object m_eventLock = new Object(); 
 
    //增加⼀個引⽤ 委托鏈表頭部的私有字段
    private EventHadler<NewMailEventArgs> m_NewMail;
 
    //增加⼀個事件成員
    public event EventHandler<NewMailEventArgs> NewMail
    {
        //顯式實現add
        add
        {
         //加私有鎖,並向委托鏈表增加⼀個處理程序以‘value’為參數
        lock(m_eventLock){m_NewMail+=value;}  
       }
      //顯式實現remove
     remove 
     {
      //加私有鎖,並向委托鏈表移除⼀個處理程序以‘value’為參數 
      lock(m_eventLock){m_NewMail -= value;}
      } 
    }
 
    //定義⼀個負責引發事件的⽅法,來通知已訂閱事件的對象事件已經發⽣,如果類是封裝的
    //則需要將⽅法聲明為private和non-virtual
    proteted virtual void OnNewMail(NewMailEventArgs e) 
    { 
      //出於線程安全考慮,將委托字段保存到⼀個臨時字段中
      EventHadler<NewMailEventArgs> temp = m_NewMail; 
      if(temp!=null){temp(this,e);} 
     } 
 
    //將輸⼊轉化為希望的事件
    public void SimulateNewMail(String from,String to,String subject) 
     {
         //構建⼀個對象存放給事件接收者的信息
        NewMailEventArgs e = new NewMailEventArgs(from,to,subject);  
        //引發 
        OnNewMail(e);  
      }
}
 
委托與事件
關鍵字“event”是個修飾詞,在絕⼤多數的情形中,被指定為委托(delegate)的對象和被指定為事件(event)的對象是可以互換的。然⽽,事件還有特殊之處:
 ● 事件就像⼀個委托類型的字段。該字段引⽤了⼀個代表事件處理器的委托,這些處理器是被添加到事件上的;
 ● 事件只能在聲明它的類中被調⽤,⽽所有能見到委托的地⽅都可以使⽤委托;
 ● 事件可以被包含在接口中⽽委托不可以;
 ● 事件有可被重寫的Add和Remove存取(acccessor)⽅法;
 

(九)、Linq表達式、異步處理

LINQ
語言集成查詢 (LINQ) 是一系列直接將查詢功能集成到 C# 語言的技術統稱,比如涵蓋:SQL 數據庫查詢、XML 文檔查詢、List對象查詢、Array對象查詢、String對象查詢……。 借助 LINQ,查詢成為了最高級的語言構造,就像類、方法和事件一樣。
 
示例:
class LINQQueryExpressions
{
    static void Main()
    {
        
        // Specify the data source.
        int[] scores = new int[] { 97, 92, 81, 60 };
 
        // Define the query expression.
        IEnumerable<int> scoreQuery =
            from score in scores
            where score > 80
            select score;
 
        // Execute the query.
        foreach (int i in scoreQuery)
        {
            Console.Write(i + " ");
        }            
    }
}
// Output: 97 92 81
 
更詳細的Linq用法請參考:
 
異步處理
異步是 .NET 中充分使用處理器核心資源的機制,異步機制直接處理多個核心上的阻塞 I/O 和並發操作以提高系統執行效率。
.NET 異步的特點:
1、等待 I/O 請求返回的同時,可通過生成處理更多請求的線程,處理更多的服務器請求。
2、等待 I/O 請求的同時生成 UI 交互線程,並通過將長時間運行的工作轉換到其他 CPU 核心,讓 UI 的響應速度更快。
 
使用基於 .NET 任務的異步模型可直接編寫綁定 I/O 和 CPU 的異步代碼。 該模型由 Task 和 Task<T> 類型以及 C# 和 Visual Basic 中的 async 和 await 關鍵字公開。 (有關特定語言的資源,請參見另請參閱部分。)
 
Task是用於實現稱之為並發 Promise 模型的構造。 簡單地說,它們“承諾”,會在稍后完成工作。
Task 表示不返回值的單個操作。
Task<T> 表示返回 T 類型的值的單個操作。
 
Task在當前線程上執行,且在適當時會將工作委托給操作系統。 可選擇性地通過 Task.Run API 顯式請求任務在獨立線程上運行。
 
示例:
//定義一個基於Task的異步方法
public Task<string> GetHtmlAsync()
{
    // Execution is synchronous here
    var client = new HttpClient();
    return client.GetStringAsync("https://www.dotnetfoundation.org");
}
 
//第二個異步方法
public async Task<string> GetFirstCharactersCountAsync(string url, int count)
{
    // Execution is synchronous here
    var client = new HttpClient();
 
    // Execution of GetFirstCharactersCountAsync() is yielded to the caller here
    // GetStringAsync returns a Task<string>, which is *awaited*
    var page = await client.GetStringAsync(url);
 
    // Execution resumes when the client.GetStringAsync task completes,
    // becoming synchronous again.
 
    if (count > page.Length)
    {
        return page;
    }
    else
    {
        return page.Substring(0, count);
    }
}
 
//調用示例
var str = await GetHtmlAsync();
var str2 = await GetFirstCharactersCountAsync("https://www.dotnetfoundation.org",100);
 
更深入地了解 .NET 上的異步編程
 

三、.NET 上的 Web 開發: ASP.NET Core

.NET上的Web解決方案由ASP.NET Core 框架實現,某種程度上你可以將之理解為Java界的Spring MVC。ASP.NET 是經典.NET上的Web解決方案,我們建議新的Web應用應該選擇ASP.NET Core。
當前Web開發存在兩種主要的風格:MVC,Web API。MVC指的是模型--視圖--控制器的Web程序設計模式,而Web API指的是面向RESTful API場景的Web程序設計模式,它僅提供API調用的響應而不關心視圖。
 
ASP.NET Core
ASP.NET Core MVC 框架由如下基本組件構成:
路由
模型綁定
模型驗證
依賴關系注入
篩選器
區域
Web API
Razor 視圖引擎
強類型視圖
標記幫助程序
視圖組件
 
控制器:ASP.NET Core MVC 的Web請求入口是由Controller類型或其子類型的公共方法實現的,一般情況下每個請求入口都是一部分業務邏輯代碼的聚合。
 
例:
public class DefaultController : ControllerBase
{
    public ActionResult<string> Index()
    {
        return "hello,world";
    }
}
 
路由:ASP.NET Core MVC 建立在 ASP.NET CORE 的路由之上,是一個功能強大的 URL 映射組件,可用於生成具有易於理解和可搜索 URL 的應用程序。 它可讓你定義適用於搜索引擎優化 (SEO) 和鏈接生成的應用程序 URL 命名模式,而不考慮如何組織 Web 服務器上的文件。 可以使用支持路由值約束、默認值和可選值的方便路由模板語法來定義路由。
 
例:
routes.MapRoute(name: "Default", template: "{controller=Home}/{action=Index}/{id?}");
 
模型:ASP.NET Core MVC 模型綁定將客戶端請求數據(窗體值、路由數據、查詢字符串參數、HTTP 頭)轉換到控制器可以處理的對象中。 因此,控制器邏輯不必找出傳入的請求數據;它只需具備作為其操作方法的參數的數據。
 
例:
public async Task<IActionResult> Login(LoginViewModel model, string returnUrl = null) { ... }
 
模型驗證:ASP.NET Core MVC  通過使用數據注釋驗證屬性修飾模型對象來支持驗證。 驗證屬性在值發布到服務器前在客戶端上進行檢查,並在調用控制器操作前在服務器上進行檢查。
 
例:
using System.ComponentModel.DataAnnotations;
public class LoginViewModel
{
    [Required]
    [EmailAddress]
    public string Email { get; set; }
 
    [Required]
    [DataType(DataType.Password)]
    public string Password { get; set; }
 
    [Display(Name = "Remember me?")]
    public bool RememberMe { get; set; }
}
 
Razor視圖引擎: Razor 是一種緊湊、富有表現力且流暢的模板標記語言,用於使用嵌入式 C# 代碼定義視圖。 Razor 用於在服務器上動態生成 Web 內容。 可以完全混合服務器代碼與客戶端內容和代碼。我們可以在MVC工程中,往Controller添加請求入口的View文件,這些View文件代表視圖文件(.cshtml),這些文件默認使用Razor視圖引擎來實現服務端渲染視圖。
例:
Index.cshtml:
 
<!-- 單行代碼塊 -->
@{ var myMessage = "Hello World"; }
 
<!-- 行內表達式或變量 -->
<p>The value of myMessage is: @myMessage</p>
 
<!-- 多行代碼塊 -->
@{
var greeting = "Welcome to our site!";
var weekDay = DateTime.Now.DayOfWeek;
var greetingMessage = greeting + " Today is: " + weekDay;
}
<p>The greeting is: @greetingMessage</p>
 
 
Web API:  ASP.NET Core 支持使用 C# 創建 RESTful 服務,也稱為 Web API。 Web API 使用控制器響應這些請求,Web API 中的控制器是派生自 ControllerBase 的類。
 
例:
[Route("api/[controller]")]
[ApiController]
public class ValuesController : ControllerBase
{
    public ActionResult<Pet> Create(Pet pet)
    {
        pet.Id = _petsInMemoryStore.Any() ? _petsInMemoryStore.Max(p => p.Id) + 1 : 1;
        _petsInMemoryStore.Add(pet);
 
        return CreatedAtAction(nameof(GetById),
        new { id = pet.Id }, pet);
    }
}
 
SignalR: ASP.NET Core SignalR 是一個開源代碼庫,它簡化了向應用添加實時 Web 功能的過程。 實時 Web 功能使服務器端代碼能夠即時將內容推送到客戶端。
SignalR 的適用對象:需要來自服務器的高頻率更新的應用。
 
例如:
游戲、社交網絡、投票、拍賣、地圖和 GPS 應用;
儀表板和監視應用;
協作應用,例如白板應用和團隊會議軟件;
需要通知的應用, 社交網絡、電子郵件、聊天、游戲、行程警示以及許多其他應用都使用通知;
 
SignalR 提供了一個用於創建服務器到客戶端遠程過程調用(RPC)的 API。 RPC 通過服務器端 .NET Core 代碼調用客戶端上的 JavaScript 函數。
以下是 ASP.NET Core SignalR 的一些功能:
1、自動管理連接。
2、向所有連接的客戶端廣播消息。 例如,聊天室。
3、將消息發送到特定的客戶端或客戶端組。
4、擴展以處理增加的流量。
 

 四、.NET 上的ORM

EF6 & EF Core
EntityFramework 6.x (EF6) 是經典 .NET上的 ORM 框架,它功能全面在Windows上運行穩定。
EntityFramework Core (EF Core) 是 EntityFramework 的跨平台移植版本,目前功能上與 EF6 仍有差距,可以滿足絕大部分 CRUD 操作。
下圖是 EF6 與 EF Core 在數據庫支持上的對比: 
 
其它ORM
dapper 是Stack Overflow貢獻的輕量級 ORM 框架,兼容.NET Core 和 .NET 4.5x,它直接擴展了.NET Connection 對象。
SmartSql 是一個包括ORM及延伸功能的數據、緩存讀寫與配置框架。
 
以上介紹的主要的ORM工具都可以在Github上找到其官方主頁。
 

五、.NET 微服務和容器化

.NET Core 是最早響應微服務與容器化部署的技術平台。.NET 團隊在Docker Hub 官網上維護着所有主要的 .NET Core 版本的 Docker 鏡像。
你可以在這個鏈接上找到這些鏡像: https://hub.docker.com/_/microsoft-dotnet-core
 
值得一提的是,.NET Core 在 Docker 上的性能表現超過了大部分其他同類技術平台。例如使用 Raygun 工具測試相同 Linux 環境的上運行的 Node.js 與 .NET Core 的性能對比,.NET Core 的性能是 Node.js 的2000%。
 
.NET Core 是天生為雲計算優化的技術平台,有着優良的可伸縮性,並兼容主流的雲計算平台,比如 Azure、AWS、阿里雲。
 

上圖是 .NET Core 上實現的微服務與 docker 容器部署的典型架構示例

 
關於如何設計、發布 .NET Core 的微服務到 Docker 鏡像,可以下載這個官方中文說明書: https://dotnet.microsoft.com/download/thank-you/microservices-architecture-ebook-zh-cn

六、.NET平台與Java平台的互換性

  .NET Java    

包管理

nuget Maven
Web場景開發
ASP.NET
ASP.NET Core
Spring Boot
ORM
EntityFramework 6.x
EntityFramework Core
dapper
NHibernate
SmartSql
Hibernate
Mybatis
 
單元測試
MSUnit
XUnit.net
JUnit
Android/ios 開發 Xamarin
Android SDK
RoboVM
Windows 開發
.NET Framework 4.x
.NET Core 3.0+
Mono
Oracle JDK
Open JDK (free)
Mac OS 開發
Mono
Xamarin/.NET Core
Oracle JDK
Open JDK(free)
linux開發
Mono
.NET Core
Oracle JDK
Open JDK(free)
docker支持
.NET Core
ASP.NET Core
Mono
Oracle JDK
Open JDK(free)
AI/數據分析
ML.net
ONNX Runtime
Microsoft Cognitive Toolkit(CNTK)
tensorflow.net
.NET for Apache Spark
Eclipse Deeplearning4j
Apache OpenNLP
Java-ML
Spark
Flink
Kafka
Storm
游戲開發
Unity (C#語言)
MonoGame
CRYENGINE
 
IoT .NET Core Open IoT Stack for Java

 

以上關於 .NET 平台及 Java 平台的比較信息來源於一小部分有代表性的技術棧,僅供參考。
關於 .NET 平台更多的生態內容可以參考這個鏈接: https://github.com/thangchung/awesome-dotnet-core
 
感謝 .NET 社群中的朋友幫忙審校。
 
參考鏈接:

 


免責聲明!

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



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