天天看點

帶你讀《C# 7.0核心技術指南》之一:C#和.NET Framework簡介第1章

原書第7版 點選檢視第二章

C# 7.0核心技術指南

C# 7.0 in a Nutshell

帶你讀《C# 7.0核心技術指南》之一:C#和.NET Framework簡介第1章

[美] 約瑟夫·阿壩哈瑞(Joseph Albahari)

本·阿壩哈瑞(Ben Albahari) 著

劉夏 譯

Beijing Boston Farnham Sebastopol Tokyo

O’Reilly Media, Inc.授權機械工業出版社出版

機械工業出版社

第1章

C#和.NET Framework簡介

C#是一種通用的,類型安全的面向對象程式設計語言。其目标是提高程式員的生産力,為此,需要在簡單性、表達性和性能之間進行權衡。C#語言的首席架構師Anders Hejlsberg随該語言的第一個版本一直走到了今天(他也是Turbo Pascal的發明者和Delphi的架構師)。C#語言和平台無關,且可以與諸多平台下的編譯器和架構(尤其是Windows下的Microsoft .NET Framework)協同工作。

1.1 面向對象

C#實作了豐富的面向對象範式,包括封裝、繼承和多态。封裝意味着在對象周圍建立一個邊界,将其外部(公有)行為與内部(私有)實作細節隔離。C#面向對象特性包括:

統一的類型系統

C#中的基礎構件是一種稱為類型的資料與函數的封裝單元。C#擁有統一的類型系統,其中的所有類型都共享一個公共的基類。這意味着所有類型,不論它們是表示業務對象還是表示數字這樣的基元類型,都共享相同的基本功能。例如,任何類型的執行個體都可以通過調用ToString方法将自身轉換為一個字元串。

類與接口

在傳統面向對象範式中,唯一的類型就是類。然而C#還有其他幾種類型,其中之一是接口(interface)。接口與類相似,但它僅僅描述成員。而實作接口的類型将實作接口定義的這些成員。接口在需要多繼承的情形下非常有用(與C++和Eiffel等語言不同,C#并不支援類的多繼承)。

屬性、方法和事件

在純粹的面向對象範式中,所有的函數都是方法(Smalltalk就是這樣)。而在C#中,方法隻是函數成員之一。除此之外還有屬性、事件及其他的形式。屬性是封裝了一部分對象狀态的函數成員,例如按鈕的顔色或者标簽的文本。事件則是簡化對象狀态變化處理的函數成員。

雖然C#首先是一種面向對象的語言,但它也借鑒了函數式程式設計的範式。例如:

可以将函數作為值看待

C#使用委托(delegate)将函數作為值傳遞給其他函數或者從其他函數中傳回。

C#支援純函數模式

函數式程式設計的核心是避免使用值可以變化的變量,或稱為聲明式模式。C#擁有支援該模式的若幹關鍵功能。包括支援可以捕獲變量的匿名函數(Lambda表達式),通過查詢表達式(query expression)執行清單式或響應式程式設計;它還可以使用隻讀字段(readonly field)和屬性建立不可變的(immutable)類型。

1.2 類型安全性

C#是一種類型安全(type-safe)的語言。這意味着類型的執行個體隻能通過它們定義的協定進行互動,進而保證了每種類型的内部一緻性。例如,C#不允許将字元串類型作為整數類型進行處理。

更具體地說,C#支援靜态類型化(static typing),即在編譯時會執行安全性檢查。此外,在運作時也會同樣執行類型安全性檢查。

靜态類型化能夠在程式運作之前排除大量的錯誤。它将大量的運作時單元測試轉移到編譯器中,確定程式中所有類型之間都是互相适配的,使大型程式更易于管理、更具預測性并更加健壯。而且靜态類型化可以借助一些工具,例如Visual Studio的IntelliSense來提供更好的程式設計輔助。因為它知道某個特定變量的類型,自然也知道該變量上能夠調用的方法。

C#允許部分代碼通過dynamic關鍵字來動态定義指定類型。然而,C#在大多數情況下仍然是一門靜态類型化語言。

C#還是一門強類型語言(strongly typed language),因為它的類型規則(不論是靜态還是運作時)非常嚴格。例如,不能用一個浮點類型的參數來調用一個接受整數類型參數的函數,必須顯式将這個浮點數轉換為整數。這可以防止編碼錯誤。

強類型也是C#代碼能夠在沙盒(sandbox)中運作的原因之一。沙盒環境中的所有安全性均由宿主要制。是以在沙盒中,使用者無法越過類型規則而随意破壞對象的狀态。

1.3 記憶體管理

C#依靠運作時來實作自動記憶體管理。公共語言運作時的垃圾回收器會作為程式的一部分運作,并負責回收那些不再被引用的對象所占用的記憶體,程式員無須顯式釋放對象的記憶體,進而避免諸如C++等語言中錯誤使用指針而造成的問題。

C#并未抛棄指針,隻是在大多數程式設計任務中是不需要使用指針的。對于性能優先的熱點和互操作性,仍然可以在标記為unsafe的程式塊内使用指針并進行顯式記憶體配置設定。

1.4 平台支援

C#以往幾乎隻用于編寫在Windows平台上執行的代碼。但最近,Microsoft和一些公司将其推廣到了其他的平台上,包括Linux、macOS、iOS以及Android。Xamarin可以使用C#進行跨平台的移動應用開發,而可移植的類型庫(Portable Class Library)也得到了廣泛的應用。Microsoft釋出的ASP.NET Core是一個跨平台的輕量級網絡宿主架構,并可以在.NET Framework以及.NET Core (開源的跨平台運作時)上運作。譯注1

1.5 C#和CLR

C#依賴運作時環境,它含有許多特性,如自動化記憶體管理和異常處理。Microsoft .NET Framework的核心公共語言運作時(Common Language Runtime,CLR)就提供了這些運作時特性。(.NET Core以及Xamarin架構也提供了類似的運作時。)CLR和語言無關。開發者可以使用多種語言,例如C#、F#、Visual Basic.NET,以及托管C++來建構應用程式。

與其他的托管語言一樣,C#也會将代碼編譯為托管代碼。托管代碼以中間語言(Intermediate Language,IL)的形式表示。CLR通常會在執行前,将IL轉換為機器(例如x86或x64)原生代碼,稱為即時(Just-In-Time,JIT)編譯。除此之外,還可以使用提前編譯(ahead-of-time compilation)技術來改善擁有大程式集,或在資源有限的裝置上運作的程式的啟動速度(包括那些使用Xamarin開發的)滿足iOS應用商店規則的應用。

托管代碼的容器稱為程式集(assembly)或可移植程式集(portable executable)。程式集可以是一個可執行檔案(.exe)也可以是一個庫(.dll)。它們不僅包含IL,還包含稱為中繼資料

(metadata)的類型資訊。中繼資料的引入使程式集無須額外的檔案就可以引用其他程式集中的類型。

使用Microsoft的ildasm工具可以反編譯并檢視程式集的IL。而其他工具,例如ILSpy、dotPeek(JetBrains)以及Reflector(Red Gate)則可以将IL代碼進一步反編譯為C#。IL的層次相比原生機器代碼要高得多,是以反編譯器可以高品質地重建C#代碼。

程式也可以通過反射(reflection)查詢其中繼資料,甚至在運作時生成新的IL(reflection.emit)。

1.6 CLR和.NET Framework

.NET Framework是由CLR和大量的程式庫組成的。這些程式庫由核心庫(本書主要介紹)和應用庫組成,應用庫依賴于核心庫。圖1-1是這些程式庫的可視化概況(也可以作為本書的導航圖)。

帶你讀《C# 7.0核心技術指南》之一:C#和.NET Framework簡介第1章

核心庫又稱為基礎類庫(Base Class Library, BCL)。而整個架構稱為架構類庫(Framework Class Library)。

1.7 其他架構

Microsoft .NET Framework是一個全面而成熟的架構,但僅僅運作在Microsoft Windows(桌面版本和伺服器)上。在若幹年裡,陸續出現了支援其他平台的架構。目前除.NET Framework之外,還存在三個其他的架構。而且這三個架構都是屬于Microsoft的。

Universal Windows Platform (UWP)

為了編寫Windows 10應用商店的應用,并使其可以在支援Windows 10的裝置(手機、XBox、Surface Hub以及Hololens)上執行。應用必須在沙盒中執行以降低惡意軟體的威脅,并防止類似讀寫任意檔案這樣的操作。

.NET Core以及ASP.NET Core

這是一個用來開發易于部署的Internet應用程式和微服務的開源架構(最初基于一個削減功能後的.NET Framework)。用它書寫的應用可以在Windows、macOS以及Linux上運作。與.NET Framework不同,.NET Core可以将Web應用程式打包并進行xcopy部署(自包含的部署)。

Xamarin

該架構用于書寫iOS、Android以及Windows Mobile等移動應用。微軟已于2016年将Xamarin公司收購。

表1-1比較了上述幾種架構所支援的平台。

帶你讀《C# 7.0核心技術指南》之一:C#和.NET Framework簡介第1章

四種架構支援的平台、基礎庫和應用場景各有不同。但是可以說在.NET Core 2.0釋出之後,它們都公布了相似的核心架構(BCL),而這也是本書的着眼點。甚至我們可以利用這種共性,書寫可以在所有四種架構上工作的類庫(有關.NET Standard 2.0的内容請參見第5章)。

UWP在内部使用了.NET Core,是以從技術上說,.NET Core可以在Windows 10裝置上運作(盡管這與為ASP.NET Core提供架構的目的不同)。這和表1-1的内容有些出入。可以預測,未來.NET Core 2會得到更加廣泛的使用。

1.7.1遺留架構和小衆架構

以下的架構仍然可以在舊有的平台上運作:

  • 用于Windows 8/8.1的Windows Runtime(現被UWP取代)。
  • Windows Phone 7/8(現被UWP取代)。
  • 用于遊戲開發的Microsoft XNA(現被UWP取代)。
  • Silverlight(由于HTML5和JavaScript的興起而不再繼續開發)。
  • .NET Core 1.x(.NET Core 2.0的前序版本,其功能非常有限)。

同樣值得一提的是以下的小衆架構:

  • .NET Micro Framework是在資源非常受限的嵌入式裝置上運作.NET代碼的架構(大小在1MB以内)。
  • Mono是Xamarin開發的開源架構,包含了用于開發跨平台(Linux、macOS以及Windows)桌面應用的類庫。該架構并未支援所有的特性。

除此之外,我們還可以在SQL Server上執行托管代碼。SQL Server的CLR內建環境支援在SQL中調用C#開發的自定義函數、存儲過程以及聚合函數。它雖然使用.NET Framework,但是其沙盒是由特殊的CLR宿主提供的,以保護SQL Server程序無恙。

1.7.2 Windows Runtime

C#還支援和Windows Runtime (WinRT)的互操作。WinRT是

  • 支援Windows 8及以上作業系統的語言無關的面向對象的執行接口。
  • 植入Windows 8及以上作業系統的庫。該類庫與上述接口相容。

“WinRT”這個詞彙容易令人誤解。這是因為它曾經有兩個含義:

  • 其一是UWP的前身。即Windows 8/8.1商店應用(有時稱為Metro或者Modern應用)的開發平台。
  • 其二是Microsoft在2011年釋出的基于精簡指令集(RISC)的平闆電腦作業系統(該系統已經被廢棄)。

所謂執行接口(execution interface)是一個調用(潛在的)由其他語言書寫的代碼的協定。Microsoft Windows曾以低層次的C語言形式提供了原生的執行接口,組成了Win32 API。

WinRT則更加豐富。從局部看,它是一個支援.NET、C++和JavaScript的增強版本的COM(元件對象模型)。和Win32不同,它是面向對象的,并擁有相對豐富的類型系統。這意味着從C#中引用WinRT庫就像是引用.NET庫一樣,你甚至不會意識到你正在使用WinRT。

Windows 10的WinRT庫是UWP平台的關鍵組成部分(UWP依托于WinRT和.NET Core庫)。如果目标平台是标準.NET Framework平台,則引用Windows 10的WinRT庫是可選的。但是當需要通路Windows 10特有的,并未被.NET Framework涵蓋的功能時,WinRT庫就非常有用了。

Windows 10的WinRT庫支援UWP使用者界面,可開發沉浸式觸摸優先的應用。它還支援移動裝置相關的功能,例如傳感器、文本消息等(Windows 8、8.1以及10的新功能是通過WinRT而非Win32開放的)。WinRT庫還提供了檔案I/O定制功能使其能夠在UWP沙盒中順暢運作。

WinRT和普通COM的差別是WinRT的程式庫支援多種語言,包括C#、VB、C++和JavaScript,是以每一種語言(幾乎)都将WinRT類型視為自己的專屬類型。例如,WinRT将根據目智語言的要求調整大小寫規則,甚至還會重新對一些函數與接口進行映射。WinRT程式集還在.winmd檔案中包含了豐富的中繼資料,而其格式與.NET檔案相同,不需要特殊處理就可以無縫對接。事實上,除了命名空間存在差別之外,開發者甚至不知道使用的是WinRT而非.NET類型。此外,WinRT類型遵循COM風格限制,并對繼承和泛型提供了有限的支援。

C#不僅可以消費WinRT庫,還可以建立新的庫(并在JavaScript應用程式中調用)。

1.8 C#簡史

下文将倒序介紹C#各個版本的新特性以友善熟悉舊版本語言的讀者。

1.8.1 C# 7.0新特性

(C# 7.0随Visual Studio 2017釋出。)

1.8.1.1 數字字面量的改進

C# 7中,數字字面量可以使用下劃線來改善可讀性、它們稱為數字分隔符而被編譯器忽略:

int million = 1_000_000;

二進制字面量可以使用0b字首進行辨別:

var b = 0b1010_1011_1100_1101_1110_1111;

1.8.1.2 輸出變量及參數忽略

C# 7中,調用含有out參數的方法将更加容易。首先,可以非常自然地聲明輸出變量:

bool successful = int.TryParse ("123", out int result);

Console.WriteLine (result);

當調用含有多個out參數的方法時,可以使用下劃線字元忽略你并不關心的參數:

SomeBigMethod (out _, out _, out _, out int x, out _, out _, out _);

Console.WriteLine (x);

1.8.1.3 模式

is運算符也可以自然地引入變量了,稱為模式變量(請參見3.2.2.5節):

void Foo (object x)

{

if (x is string s)

Console.WriteLine (s.Length);           

}

switch語句同樣支援模式,是以我們不僅可以選擇常量還可以選擇類型(請參見2.11.3.5節);可以使用when子句來指定一個判斷條件;或是直接選擇null:

switch (x)

case int i:

Console.WriteLine ("It's an int!");
break;           

case string s:

Console.WriteLine (s.Length);   // We can use the s variable
break;           

case bool b when b == true: // Matches only when b is true

Console.WriteLine ("True");
break;           

case null:

Console.WriteLine ("Nothing");
break;           

1.8.1.4 局部方法

局部方法是聲明在其他函數内部的方法(請參見3.1.2.4節):

void WriteCubes()

Console.WriteLine (Cube (3));

Console.WriteLine (Cube (4));

Console.WriteLine (Cube (5));

int Cube (int value) => value value value;

局部方法僅僅在其包含函數内可見,它們可以像Lambda表達式那樣捕獲局部變量。

1.8.1.5 更多的表達式體成員

C# 6引入了以“胖箭頭”文法表示的表達式體的方法、隻讀屬性、運算符以及索引器。而C# 7更将其擴充到了構造函數、讀/寫屬性和終結器中:

public class Person

string name;

public Person (string name) => Name = name;

public string Name

{

get => name;
set => name = value ?? "";           

~Person () => Console.WriteLine ("finalize");

1.8.1.6 解構器

C# 7引入了解構器模式。構造器一般接受一系列值(作為參數)并将其指派給字段,而解構器則正相反,它将字段反向指派給變量。以下示例為Person類書寫了一個解構器(不包含異常處理):

public void Deconstruct (out string firstName, out string lastName)

int spacePos = name.IndexOf (' ');

firstName = name.Substring (0, spacePos);

lastName = name.Substring (spacePos + 1);

解構器以特定的文法進行調用:

var joe = new Person ("Joe Bloggs");

var (first, last) = joe; // Deconstruction

Console.WriteLine (first); // Joe

Console.WriteLine (last); // Bloggs

1.8.1.7 元組

也許對于C# 7來說最值得一提的改進當屬顯式的元組(tuple)支援(請參見4.10節)。元組提供了一種存儲一系列相關值的簡單方式:

var bob = ("Bob", 23);

Console.WriteLine (bob.Item1); // Bob

Console.WriteLine (bob.Item2); // 23

C#的新元組實質上是使用System.ValueTuple<...>泛型結構的文法糖。多虧了編譯器的“魔力”,我們還可以對元組的元素進行命名:

var tuple = (Name:"Bob", Age:23);

Console.WriteLine (tuple.Name); // Bob

Console.WriteLine (tuple.Age); // 23

有了元組,函數再也不必通過一系列out參數來傳回多個值了:

static (int row, int column) GetFilePosition() => (3, 10);

static void Main()

var pos = GetFilePosition();

Console.WriteLine (pos.row); // 3

Console.WriteLine (pos.column); // 10

元組隐式地支援解構模式,是以很容易解構為若幹獨立的變量。是以,上述Main方法中的GetFilePosition傳回的元組将存儲于兩個局部變量row和column中:

(int row, int column) = GetFilePosition(); // Creates 2 local variables

Console.WriteLine (row); // 3

Console.WriteLine (column); // 10

1.8.1.8 throw表達式

在C# 7之前,throw一直是一個語句。現在,它也可以作為表達式出現在表達式體函數中:

public string Foo() => throw new NotImplementedException();

throw表達式也可以出現在三無判斷運算符中:

string Capitalize (string value) =>

value == null ? throw new ArgumentException ("value") :

value == "" ? "" :

char.ToUpper (value[0]) + value.Substring (1);

1.8.1.9 其他改進

C#還包含一系列針對特定的場景進行專門的微小優化的功能(請參見2.8.5節和2.8.6節)。同時,我們可以在異步方法聲明中包含傳回類型而非Task/Task。

1.8.2 C# 6.0新特性

随Visual Studio 2015一起釋出的C# 6.0采用了下一代的,完全使用C#編寫的編譯器,即“Roslyn”項目。新的編譯器将一整條編譯流水線通過程式庫進行開放,使得對各種源代碼進行分析成為可能(見第27章)。編譯器本身是開源的,可以從github.com/dotnet/roslyn獲得其源代碼。

此外,C# 6.0為了改善代碼的清晰性引入了一系列小而精的改進。

null條件(“Elvis”)運算符(請參見2.10節)可以避免在調用方法或通路類型的成員之前顯式地編寫用于null判斷的語句。在以下示例中,result将會為null而不會抛出NullReferenceException:

System.Text.StringBuilder sb = null;

string result = sb?.ToString(); // result is null

表達式體函數(expression-bodied function)(請參見3.1.2節)可以以Lambda表達式的形式書寫僅僅包含一個表達式的方法、屬性、運算符以及索引器,使代碼更加簡短:

public int TimesTwo (int x) => x * 2;

public string SomeProperty => "Property value";

屬性初始化器(property initializer,參見第3章)可以對自動屬性進行初始指派:

public DateTime TimeCreated { get; set; } = DateTime.Now;

這種初始化也支援隻讀屬性:

public DateTime TimeCreated { get; } = DateTime.Now;

隻讀屬性也可以在構造器中進行指派,這令建立不可變(隻讀)類型變得更加容易了。

索引初始化器(index initializer)(見第4章)可以一次性初始化具有索引器的任意類型:

var dict = new Dictionary()

[3] = "three",

[10] = "ten"

};

字元串插值(string interploation)(參見2.6.2節)用更加簡單的方式替代了string.Format:

string s = $"It is {DateTime.Now.DayOfWeek} today";

異常過濾器(exception filters)(請參見4.5節)可以在catch塊上再添加一個條件:

string html;

try

html = new WebClient().DownloadString ("http://asef");

catch (WebException ex) when (ex.Status == WebExceptionStatus.Timeout)

...

using static(參見2.12節)指令可以引入一個類型的所有靜态成員,這樣就可以不用書寫類型而直接使用這些成員:

using static System.Console;

...

WriteLine ("Hello, world"); // WriteLine instead of Console.WriteLine

nameof(見第3章)運算符傳回變量、類型或者其他符号的名稱。這樣在Visual Studio中就可以避免變量重命名造成不一緻的代碼:

int capacity = 123;

string x = nameof (capacity); // x is "capacity"

string y = nameof (Uri.Host); // y is "Host"

最後值得一提的是,C# 6.0可以在catch和finally塊中使用await。

1.8.3 C# 5.0新特性

C# 5.0最大的新特性是通過兩個關鍵字,async和await,支援異步功能(asynchronous function)。異步功能支援異步延續(asynchronous continuation),進而簡化響應式和線程安全的富用戶端應用程式的編寫。它還有利于編寫高并發和高效的I/O密集型應用程式,而不需要為每一個操作綁定一個線程資源。

第14章将詳細介紹異步功能。

1.8.4 C# 4.0新特性

C# 4.0增加的新特性有:

動态綁定

可選參數和命名參數

用泛型接口和委托實作類型變化

改進COM互操作性

動态綁定(參見第4章和第20章)将綁定過程(解析類型與成員的過程)從編譯時推遲到運作時。這種方法适用于一些需要避免使用複雜反射代碼的場合。動态綁定還适合于實作動态語言以及COM元件的互操作。

可選參數(見第2章)允許函數指定參數的預設值,這樣調用者就可以省略一些參數,而命名參數則允許函數的調用者按名字而非按位置指定參數。

類型變化規則在C# 4.0進行了一定程度的放寬(見第3章和第4章),是以泛型接口和泛型委托類型參數可以标記為協變(covariant)或逆變(contravariant),進而支援更加自然的類型轉換。

COM互操作性(見第25章)在C# 4.0中進行了三個方面的改進。第一,參數可以通過引用傳遞,并無須使用ref關鍵字(特别适用于與可選參數一同使用)。第二,包含COM 互操作(interop)類型的程式集可以連結而無須引用。連結的互操作類型支援類型相等轉換,無須使用主互操作程式集(Primary Interop Assembly),并且解決了版本控制和部署的難題。第三,連結的互操作類型中的函數若傳回COM變體類型,則會映射為dynamic而不是object,是以無須進行強制類型轉換。

1.8.5 C# 3.0新特性

C# 3.0增加的特性主要集中在語言內建查詢(Language Integrated Query, LINQ)上。LINQ令C#程式可以直接編寫查詢并以靜态方式檢查其正确性。它可以查詢本地集合(如清單或XML文檔),也可以查詢遠端資料源(如資料庫)。C# 3.0中和LINQ相關的新特性還包括隐式類型局部變量、匿名類型、對象構造器、Lambda表達式、擴充方法、查詢表達式和表達式樹。

隐式類型局部變量(var關鍵字,見第2章)允許在聲明語句中省略變量類型,然後由編譯器推斷其類型。這樣可以簡化代碼并支援匿名類型(見第4章)。匿名類型是一些即時建立的類,它們常用于生成LINQ查詢的最終輸出結果。數組也可以隐式類型化(見第2章)。

對象初始化器(見第3章)允許在調用構造器之後以内聯的方式設定屬性,進而簡化對象的構造過程。對象初始化器不僅支援命名類型也支援匿名類型。

Lambda表達式(見第4章)是由編譯器即時建立的微型函數,适用于建立“流暢的”LINQ查詢(見第8章)。

擴充方法(見第4章)可以在不修改類型定義的情況下使用新的方法擴充現有類型,使靜态方法變得像執行個體方法一樣。LINQ表達式的查詢運算符就是使用擴充方法實作的。

查詢表達式(見第8章)提供了編寫LINQ查詢的更進階文法,大大簡化了具有多個序列或範圍變量的LINQ查詢的編寫過程。

表達式樹(見第8章)是指派給一種特殊類型Expression的Lambda表達式的DOM(文檔對象模型,Document Object Model)模型。表達式樹使LINQ查詢能夠遠端執行(例如在資料庫伺服器上),因為它們可以在運作時進行轉換和翻譯(例如變成SQL語句)。

C# 3.0還添加了自動化屬性和分部方法。

自動化屬性(見第3章)對在get/set中對私有字段直接讀寫的屬性進行了簡化,并将字段的讀寫邏輯交給編譯器自動生成。分部方法(Partial Method,見第3章)可以令自動生成的分部類(Partial Class)自定義需要手動實作的鈎子函數,而該函數可以在沒有使用的情況下“消失”。

1.8.6 C# 2.0新特性

C# 2提供的新特性包括泛型(見第3章)、可空類型(nullable type)(見第4章)、疊代器(見第4章)以及匿名方法(Lambda表達式的前身)。這些新特性為C# 3引入LINQ鋪平了道路。

C# 2還添加了分部類、靜态類以及許多細節功能,例如對命名空間别名、友元程式集和定長緩沖區的支援。

泛型需要在運作時仍然能夠確定類型的正确性,是以需要引入新的CLR(CLR 2.0)才能達成該目标。

繼續閱讀