帶著問題讀CLR via C#(筆記二)類型基礎(chǔ)
Q1: Object類型包含哪些方法?
A1: Object類型共包含6個方法,Equals, GetHashCode, ToString, GetType, MemberwiseClone和Finalize.
Q2: new一個對象的過程是什么?
A2: 1)計算對象所需字節(jié)數(shù),包括該類型及其基類型定義的所有實例字段所需的字節(jié)數(shù)和類型對象指針、同步塊索引所需字節(jié)數(shù),類型指針和同步塊索引是CLR用來管理對象的;2)在托管堆上分配該對象所需內(nèi)存空間;3)初始化類型對象指針和同步塊索引;4)執(zhí)行構(gòu)造函數(shù)。大多數(shù)編譯器都在構(gòu)造函數(shù)中自動生成一段代碼調(diào)用基類構(gòu)造函數(shù),每個類型的構(gòu)造函數(shù)在執(zhí)行時都會初始化該類型定義的實例字段。5)返回指向新建對象的一個引用,保存在對象變量中。
可用如下代碼驗證第四步:
View Code
class Program
{
static void Main(string[] args)
{
TestThree t = new TestThree();
Console.Read();
}
}
class Test
{
int i;
public int I { get; set; }
public Test()
{
Console.WriteLine("This is Test's constructor");
}
}
class TestTwo : Test
{
public TestTwo()
{
Console.WriteLine("This is TestTwo's constructor");
}
}
class TestThree : TestTwo
{
public TestThree()
{
Console.WriteLine("This is TestThree's constructor");
}
}
執(zhí)行結(jié)果如下:
Q3: 父類型和子類型間如何進行轉(zhuǎn)換?
A3: C#允許將一個對象從它的本身類型轉(zhuǎn)換為它的父類型,這是安全的,不需要做任何額外操作,但要將一個對象從它的本身類型轉(zhuǎn)換為它的子類型,則必須要顯式轉(zhuǎn)換,因為可能會失敗。見代碼:
View Code
class Program
{
static void Main(string[] args)
{
Person person = new Person();
Man man = new Man();
Person p = man;
Man m = person;
}
}
class Person
{ }
class Man : Person
{ }
這段代碼是無法編譯通過的,在Main方法的第四行會報一個這樣的錯誤:
Error 1 Cannot implicitly convert type 'TypeBasic.Person' to 'TypeBasic.Man'. An explicit conversion exists (are you missing a cast?) C:\Users\Allen\Documents\Visual Studio 2012\Projects\TypeBasic\TypeBasic\Program.cs 16 21 TypeBasic
很顯然,一個 “男人” 一定是一個人,故可以直接轉(zhuǎn)換,但一個 “人” 并不一定是一個 “男人”,所以必須要顯式轉(zhuǎn)換??蓪⒋a這樣改寫:
// From
Man m = person;
// To
Man m = (Man)person;
這樣就可以成功通過編譯,但是在運行的時卻拋出了異常,很顯然,Person不能被轉(zhuǎn)換為Man. 什么情況下Person可以被轉(zhuǎn)換為Man? 見如下代碼:
View Code
static void Main(string[] args)
{
Man man = new Man();
Test(man);
}
static void Test(Person p)
{
Man m = (Man)p;
}
Q4: is和as操作符的作用是什么?
A4: is操作符用來判斷一個對象是否屬于某種類型,返回一個布爾值。改寫下上例的Test方法:
View Code
static void Test(Person p)
{
if (p is Man)
{
Man m = (Man)p;
}
}
以上代碼共進行了兩次類型檢測,is操作符首先檢測p是否為Man類型,在if的方法體中進行強制轉(zhuǎn)換時,CLR會再次檢測p的類型,這對性能有一定影響。
as操作符很好的解決了這個問題,再次改寫Test方法:
View Code
static void Test(Person p)
{
Man m = p as Man;
if (m != null)
{
//...
}
}
as操作符在檢測p的類型后會直接對p進行類型轉(zhuǎn)換,返回一個Man類型的對象,若檢測出p不是Man類型,則會返回null. 整個過程只進行了一次類型檢測。
Q5: 什么是命名空間?
A5: 命名空間是對類型的邏輯分組,對于編譯器而言,命名空間的作用是使類型名稱變得更長更具唯一性,但CLR并不知道命名空間,訪問一個類型時,CLR需要知道該類型的全名以及它所在程序集。
Q6: 命名空間和程序集之間的關(guān)系是什么?
A6: 命名空間和程序集間并沒有什么關(guān)聯(lián),同一命名空間的類型可以存在于不同程序集,同一程序集中的類型也可以屬于不同命名空間。
Q7: 分析以下代碼執(zhí)行時CLR發(fā)生的動作。
View Code
namespace TestConsole
{
class Program
{
static void Main(string[] args)
{
Employee e;
Int32 year;
e = new Employee();
e = Employee.Lookup("Joe");
year = e.GetYearsEmployed();
e.GenProgressReport();
}
}
class Employee
{
// 實例方法
public Int32 GetYearsEmployed()
{
//...
}
// 虛方法
public virtual string GenProgressReport()
{
//...
}
// 靜態(tài)方法
public static Employee Lookup(string name)
{
//...
}
}
class Manager : Employee
{
// 對父方法重寫
public override string GenProgressReport()
{
//...
}
}
}
A7:
1)CLR檢查該方法內(nèi)部引用的所有類型(Employee, Int32, Manager, String),確保定義了這些類型的程序集已成功加載;
2)CLR利用程序集的元數(shù)據(jù)提取這些類型的相關(guān)信息,并創(chuàng)建一些數(shù)據(jù)結(jié)構(gòu)來表示類型本身,如下圖所示:
3)執(zhí)行"序幕代碼",在線程棧中為局部變量分配內(nèi)存,并初始化它們,如下圖所示:
4)構(gòu)建Manager對象,在托管堆中創(chuàng)建一個Manager類型的實例,CLR會初始化該實例的類型對象指針,讓它引用與實例對應(yīng)的類型對象,本例中為Manager類型對象;此外CLR會初始化同步塊索引,并將該實例所有實例字段設(shè)為null或0,再調(diào)用構(gòu)造函數(shù),new操作符會返回該實例內(nèi)存地址,該地址保存在e中,如下圖:
5)Lookup是一個靜態(tài)方法,調(diào)用時CLR會定位定義該靜態(tài)方法的類型對應(yīng)的類型對象,然后JIT編譯器在該類型對象的方法表中查找被調(diào)用的方法的記錄項,對方法進行JIT編譯(第一次執(zhí)行),執(zhí)行編譯后的代碼。本例中,假定查出的實例是一個i額Manager類型,則在堆中創(chuàng)建一個Manager實例,用查出的信息初始化該實例,并返回它的地址儲存在e中,此時,第一個初始化的Manger對象將沒有指針指向它,它成為垃圾回收對象。見下圖:
6)GetYearsLookup是一個非虛實例方法,在調(diào)用時,JIT編譯器會找到發(fā)出調(diào)用的標量(e)的類型對應(yīng)的類型對象,本例中為Employee類型對象,因為e被定義為了Employee類型。如果Employee中沒有定義該方法,則會繼續(xù)向上一層查找,知道查找到Object類型對象,查找到該方法后,JIT編譯器對其進行編譯(第一次執(zhí)行),再執(zhí)行變異后的代碼,將執(zhí)行結(jié)果保存在局部變量中。見下圖:
7)GetProgressReport為定義在Employee中的虛方法,調(diào)用一個虛方法,JIT編譯器會在方法中生成一些額外代碼,這些代碼在每次調(diào)用方法時都會執(zhí)行。它首先會檢測發(fā)出調(diào)用的變量,根據(jù)地址查找到發(fā)出調(diào)用的實例,本例為一個Manager對象,然后檢測對象內(nèi)部的類型指針,找到該對象的實際類型,從實際類型對象的方法列表中查找調(diào)用的方法的記錄項,進行JIT編譯(第一次執(zhí)行),執(zhí)行變異后代碼。見下圖:
Q8: 如何理解類型對象?
A8: 類型對象本質(zhì)上也是對象,它也包含類型對象指針成員,CLR創(chuàng)建這些類型對象時,也會對其進行初始化。CLR開始在一個進程中運行時,會立即為MSCorLib.dll中定義的System.Type對象創(chuàng)建一個特殊的類型對象,Q7中的Emloyee和Manager都是Type類型的“實例”,它們的類型對象指針都會指向Type類型對象,而Type類型對象的類型對象指針則會指向自己。
相關(guān)文章
動態(tài)webservice調(diào)用接口并讀取解析返回結(jié)果
webservice的 發(fā)布一般都是使用WSDL(web service descriptive language)文件的樣式來發(fā)布的,在WSDL文件里面,包含這個webservice暴露在外面可供使用的接口。今天我們來詳細討論下如何動態(tài)調(diào)用以及讀取解析返回結(jié)果2015-06-06c# 通過內(nèi)存映射實現(xiàn)文件共享內(nèi)存的示例代碼
這篇文章主要介紹了c# 通過內(nèi)存映射實現(xiàn)文件共享內(nèi)存的示例代碼,幫助大家更好的理解和學(xué)習(xí)使用c#,感興趣的朋友可以了解下2021-04-04C#實現(xiàn)獲取多維數(shù)組的行數(shù)與列數(shù)
這篇文章主要為大家詳細介紹了C#如何分別使用Array.GetUpperBound方法和Array.GetLength方法實現(xiàn)獲取多維數(shù)組的行數(shù)與列數(shù),需要的可以參考下2024-02-02