標籤:發送 資訊 顯示 ack each execution 生命週期 輕量 模組
(已淘汰)
進階特性,多線程編程,單元測試;
第一部分 .net進階特性
1.委託:提供安全的函數回調機制;
*基本原理:與C++中的函數指標相似;安全--它和其他所有.net成員一樣是一種類型,任何委託都是System.Delegate的某個衍生類別對象;
System.Object->SystemDelegate(ISerializable,ICloneable)->System.MultiCastDelegate
->Delegate T;
public delegate void TestDelegate(inti)定義一個委託,內部包含invoke方法(由編譯器自動完成);委託的調用其實就是執行類定義委託時產生的Invoke方法;
總結:每個委託至少包含一個指向回調方法的指標,該方法可以是執行個體方法,也可以是靜態方法。委託實現回調方法的機制,方便使用;
*委託回調靜態方法和執行個體方法的區別;
當委託綁定靜態方法時,target為null;綁定執行個體方法是,target指向該執行個體方法所屬類型的一個執行個體對象;
*什麼是鏈式委託:指一個由委託組成的鏈表,而不是指另一個特殊的委託;prev,當一個委託被調用時,所有鏈表上的該委託的後續委託均被順序執行;
*鏈式委託的執行順序:按照委託鏈上的順序從當前委託開始依次向後執行。如果有需要,可以通過GetInvocationList()來獲得委託鏈上所有需要執行的委託,並且按照任何希望的順序去執行;
*可否定義有傳回值的方法的委託鏈:委託可以是帶有傳回值的方法,但多於一個待傳回值的方法被添加到委託鏈是,程式員需要手動調用每一個委託鏈上的方法;
*委託的應用場合:任務的執行者把細節工作進行再分配,執行者確切地知道什麼工作將要被執行,但卻把執行細節委託給其他組件,方法或程式集;一些簡單的重點記錄:(給個程式集中有多個模組的例子)
2.事件(Event)
定義:是一種使對象或類能夠提供通知的成員,用戶端可以通過提供事件處理常式為相應的事件添加可執行代碼;
event標記__委託類型(如EventHandler)__聲明的事件對象;
使用事件的步驟:
如果需要,定義一個派生自System.EventArgs的參數類型;
ConsoleEventArgs
在事件的管理類型中定義事件的私人成員; ConsoleManagsder.ConsoleEvent
通知事件訂閱者; ConsoleManager.SendConsoleEvent
事件使用客訂閱/取消訂閱事件; Log
補充StreamWriter,StreamReader,各種Stream:
using(FileStream fs = File.Create(...)){}
using(StreamWriter sw =info.AppendText()){}
*事件和委託有何聯絡?
實際上,事件就是就是一個委託類型,當程式員定義一個事件的時候,實際上是定義了一個特殊的委託成員。它沒有傳回值,有固定的兩個參數sender,EventArgs;而事件的使用者訂閱事件時,本質就是把事件處理方法加入到委託鏈表中;聲明event時預設會添加一對針對特定委託的add/remove方法;
*如何設計一個帶很多事件的類型?使用EventHandlerList(System.ComponentModel);
注意:考慮線程同步措施;每個事件定義一套成員--事件的委託原型,事件的訂閱和取消訂閱方法,定義事件的專用參數類型;減少多事件類型的大小,但代碼量增加;
*用代碼錶示如下情形:貓叫,老鼠逃跑,主人驚醒<==>貓叫被老鼠和主人訂閱;
3.反射
*反射的原理和實現它的基石:反射是一種動態剖析器集,模組,類型,欄位等目標對象的機制,它的實現依託於中繼資料(基石)。中繼資料是儲存在PE檔案中的資料區塊,他詳細記錄了程式集或模組內部的結構,參考型別,資訊清單.TypeDef,TypeRef&AssemblyRef,Assembly;
*.net提供哪些類型來實現反射:Assembly,AssemblyName,EventInfo,LocalVariableInfo,ManifestResourceInfo,MemberInfo,MethodBase,MethodBody,Module,ParameterInfo,PropertyInfo;
*如何動態發射程式集:動態產生一個程式集。而不僅僅是剖析器經濟或者產生程式集對象;在System.Reflection.Emit命名空間下,定義了一系列用於動態發射程式集,模組,類型,方法等元素的中間代碼。這些類型的主要使用者是編譯器,特殊反射工具或者指令碼解譯器.
AssemblyBuilder,ConstructorBuilder,CustomerAttributeBuilder,EnumBuilder,EventBuilder,FieldBuilder,GenericTypeParameterBuilder,ILGenerator,LocalBuilder,MethodBuilder,OpCodes,ParameterBuilder,PropertyBuilder,TypeBuilder;
//步驟:
在當前appDomain中定義新程式集: AppDomain myDomain = Thread.GetDomain();
定義模組: assemblyBuilder.DefineDynamicModule();
定義模組中的類型: Typebuilder addClass=assemblyModule.DefineType();
定義公用構造方法及其參數 Type[] ctorParams = new Type[]{typeof(long),typeof(long)};
構造方法中間代碼 ctorIL.Emit(OpCodes.Ldarg_0);
動態發送程式集:
object ptInstance=Activator.CreateInstance(type,ctorParams);
MethodInfo info = type.GetMethod("GetResult", newType[0]);//通過方法名和傳回值類型得到方法;
OpCodes類型包含大多數需要使用的中間代碼指令;
注意:新的程式集發射是直接把中間代碼在記憶體中產生一種機制,而不是在物理硬碟上產生代碼的機制;
*利用反射來實現工程模式,動手;
//針對每一個product做相應的Attribute,針對product系列做Attribute,還是需要參考
[AttributeUsage(AttributeTargets.Class)]
public classProductAttribute : Attribute
{RoomPart_myRP = new RoomPart();
public ProductAttribute(RoomPart rp)
{_myRP = rp; }
public RoomPart MyRoomPart
{get{ return _myRP;}
} }
[AttributeUsage(AttributeTargets.Interface)]
public classProductListAttribute : Attribute
{Type[]_mylist;
public ProductListAttribute(Type[] productList)
{_mylist = productList;}
public Type[] MyProductList
{get { return _mylist; } }
}
public IProduct Produce(RoomPart rp)
{//通過反射,從IProduct介面獲得屬性,從而獲得所有的產品零件列表
ProductListAttribute pla =(ProductListAttribute)Attribute.GetCustomAttribute(typeof(IProduct),typeof(ProductListAttribute));
//遍曆所有的實現產品零件類型
foreach(Type type in pla.MyProductList) {
ProductAttribute pa =(ProductAttribute)Attribute.GetCustomAttribute(type,typeof(ProductAttribute));
if (rp == pa.MyRoomPart) {
object pro = Assembly.GetExecutingAssembly().CreateInstance(type.FullName);
return pro as IProduct;
}
return null;}}
*用較小記憶體儲存Type,Field,Method資訊:
System.RuntimeTypeHandle和System.Type的轉換;Type.GetTypeHandle(type),Type.GetTypeFromHandle(typeHandle);
System.RuntimeMethodHandle和System.Reflection.MethodInfo的轉換;
System.RuntimeFeildHandle和System.Reflection.FieldInfo的轉換;
4.特性
特性機制協助程式員以聲明的方式進行編程,而不再需要考慮實現的細節;這樣的機制有點類似AOP的編程概念;
*什麼是特性,如何自訂一個特性?
特性是一種有別於普通命令式編程的編程方式,通常稱為聲明式編程方法。所謂聲明式編程就是程式員秩序聲明某個模組會具有怎麼樣的特性,而無需關心實現;特性在被編譯時間,和傳統的命令式代碼不同,它會被以位元據的方式寫入模組檔案的中繼資料中,而在運行時被解讀使用。特性也是經常被反射機制應用的元素,因為它本身是以中繼資料形式存放的.
自訂特性:本質就是定義一個繼承自System.Attribute類的類型;
使用需要注意:特性名稱用Attribute結尾;為了方便,使用特性時可以省略最後的Attribute;屬性類別型自身也可以添加其他特性,如[AttributeUsage()].
*.net特性可以在那些元素上應用?
Assembly,Module,Class,Struct,Enum,Constructor,Method,Property,Field,Event,Interface,Parameter,Delegate,ReturnValue,GenericParameter;
對於類型,結構等元素,特性的使用可以添加在其定義上方,而對於程式集、模組等元素的特性來說,則需要顯示地告訴編譯器這些特性的作用目標;如:[assembly:MyAttribute];
可以使用AttributeUsage(AttributeTargets..)來限定特性的使用範圍;
*擷取元素已經申明特性的方法:
System.Attribute.IsDefined;
System.Attribute.GetCustomerAttribute()/GetCustomerAttributes;會尋找指定特性以及其派生特性,並且會執行個體化.若聲明多次,則報AmbiguousMatchException異常;
System.Reflection.CustomAttributeData--GetCustomAttributes;該類型使用不會導致特性的執行個體化,適合安全性要求高的系統;
注意:讀者使用這些方法時,需要注意是否需要執行個體化特性,因為這意味著中繼資料中的位元組流將被執行,這可能是一個安全隱患。(未理解)
*一個元素能否重複聲明同一特性?使用AttributeUsageAttribute的AllowMultiple屬性,設定為True;
5.名企面試真題
*.什麼是反射?一種動態剖析器集,模組,類型,方法,欄位等目標對象的機制,它依託於中繼資料;
*.在什麼情況下使用過委託(答的都不是很準確)?需要由使用者而不是設計者提供回調方法時使用。
任務的執行者把細節任務進行再分配,執行者確切知道什麼工作將要被執行,但卻把執行細節委託給其他的組件,方法或程式集。
*.請概述事件與委託有什麼不同?事件是一種指定格式的委託,要求它沒有傳回值,參數固定為object-sender,EventArgs-args,它內建add/remove方法,由於在委託鏈上添加和刪除回調方法(事件處理方法);
*.你最常用的特性有哪些?首先是特性的特性:[AttributeUsage],[Serializable]等;
*.介紹一個你設計過的自訂特性,為什麼要使用特性?
如[target.class]NameAttribute,使用特性有很大的靈活性,比如對工廠設計模式的最佳化,達到解耦的作用;同時特性是一種聲明式的編程方式;
*.反射機制的效能如何,你會在什麼情況下使用反射?反射是一種動態剖析器集,模組,類型,方法等目標對象的機制,它的基石是中繼資料;其實他就是通過調用方法對中繼資料進行操作,使用它會使程式效能下降。
當我需要對暫時未知的程式集,類型等目標對象進行操作時,我會使用。因為此時是沒用方法名稱,欄位名稱等內容的。在一些特殊情況下,我還會有Reflection.Emit動態在記憶體中建立程式集;
*.請問動態發射代碼有何作用?可以直接在記憶體中建立程式集,不用駐留在硬碟;它一般使用在編譯器,特殊反射工具,指令碼解譯器中;
*.請用代碼描述肯德基排隊購買情境;自己做一個簡版的;假設一個收銀,來人排入佇列,買好離開隊列;不涉及多線程,演算法選擇等內容;
*.請介紹組件中繼資料包含哪些內容?TypeDef,TypeRef&AssemblyRef,Assembly清單;
第二部分 .net多線程編程
1.多線程編程的基本概念;
*解釋作業系統層面上的進程和線程
進程:擁有自己的程式塊,獨佔的資源和資料,並且可以被作業系統調用;
線程:是一個可以被調度的單元,並且維護自己的堆棧和上下文環境;
簡單來說進程代表了一個正在啟動並執行應用程式實體,而進程可以包含一個或多個線程;
線程和進程最大的區別在於隔離性問題,每個進程被單獨地隔離,擁有自己的記憶體塊,獨佔資源和運行資料,進程間的互動也是相當困難的.而同一進程內的所有線程共用資源和記憶體塊,並且一個線程可以訪問,結束同一進程內的其他線程;
*多線程程式在OS中是並存執行的麼
線程調度:搶佔式和非搶佔式,例如Windows--屬於同時採用搶佔式和非搶佔式模式。對於那些優先順序高的線程,採用非搶佔,對於普通線程,採用搶佔模式快速切換;
在單個CPU的架構上,任何時候只能存在一個啟動並執行線程,OS用過快速的調度輪換讓使用者感覺多線程同時執行。而在多CPU架構上,則可能存在並行啟動並執行線程,這取決與線程間是否爭用資源;(windows提出一個超執行緒的概念,就是虛擬CPU,多通道(Intel)?)
*什麼是遷程?可以視為一個輕量級線程,擁有自己的棧和上下文(寄存器)狀態,調度由程式員編碼控制;
在.net運行架構中建立Thread,並不一定保證在OS層面上產生了一個真正的線程;想想(os線程,使用者線程);
實際上,.net中的線程可能是一個線程,一個遷程甚至一個.net自訂的結構;
補充:所謂CLR寄宿,指CLR架構運行在某個應用程式上而非位元組在作業系統上。常見的有asp.net,sqlserver2005.
2.net中的多線程編程;
*如何在.net中手動控制多個線程;建立一個Thread類型對象並不意味著產生一個線程,需要調用Start才產生;
控制線程的狀態:
*如何使用.net線程池;
所謂的.net線程池,是指由CLR管理的線程池,而不是指線程池是由.net架構引入的;CLR管理代碼負責整理並處理線程的需求,策略可變,投遞需求較多時,可能多個線程同時運行處理需求,反之,只建立單線程。
線程池中啟動並執行線程都是後台線程,IsBackground為true;所謂後台線程指這些線程的運行不會阻礙應用程式的結束;
System.Threading.ThreadPool:每個進程都擁有一個線程池,.net提供管理機制,使用者只需要把線程需求插入到線程池即可;
static boolQueueUserWorkItem(WaitCallBack--委託類型,接受Object參數,無傳回值--callback)
static bool QueueUserWorkItem(WaitCallBack callback, Objectstate)
static bool UnsafeQueueUserWorkItem(WaitCallBack callback, Objectstate):不會將主線程許可權限制傳遞給輔助線程,可能會提升輔助線程的許可權,產生安全性漏洞;
*如何查看和設定線程池的上下限;一般不需要修改
ThreadPool.Get/Set Max/Min/Available Threads;
*如何定義線程獨享的全域資料;
TLS:本地線程儲存;靜態變數扮演了全域(appDomain)可見的資料角色,一個static變數同一appDomain的線程均可訪問,若希望只有當前線程可對其訪問修改的變數,就需要TLS的概念;
方式一,使用LocalDataStoreSlot:它本身不是線程獨顯的,但初始化一個該對象意味著在應用程式定義域內的每個線程上均分配一個資料插槽;
LocalDataStoreSlot ldss =thread.AllocateDataSlot();
Thread.SetData(ldss, Thread.CurrentThread.ManagedThreadId);
Thread.GetData(ldss);
方式二,ThreadStaticAttribute使用
*如何使用非同步模式讀取一個檔案;
非同步模式:是一種處理流類型時經常用到的模式,讀寫檔案,網路傳輸,讀寫資料庫,甚至可以非同步模式來做任何計算工作。相對於收到編寫線程代碼,非同步模式是一種高效的編程模式;
指啟動一個操作後可以繼續執行其他工作,而不必等待操作的結束。
在.net中,很多類型都支援非同步模式編程,以下為4個步驟:
調用一個形似BeginXXX的方法,表明開始非同步執行某操作;
在調用了BeginXXX方法後,主線程可以繼續執行任意的代碼,而無需關心非同步作業情況;
以非同步聚集技巧來查看非同步作業的結果;
調用EndXXX來表示一個非同步作業結果;
非同步模式區別於線程池機制的地方:
直接調用EndXXX方法,如果非同步作業還未執行,主線程會被阻止直到一步操作結束;
查看調用BeginXXX後得到的IAsyncResult對象IsCompleted屬性;
在調用BeginXXX時傳入操作結束後需要執行的方法,同時把執行非同步作業的對象傳入以便執行EndXXX方法;(未理解)
盡量使用第三種技巧:主線程負責開始非同步讀取並且傳入狙擊時需要的方法和狀態物件;
using (FileStream fs =File.Create(_fileName))
{string content = @"寶山是個SB,哈哈!";
byte[] contentByte =Encoding.Default.GetBytes(content);
fs.Write(contentByte, 0,contentByte.Length);}
//開始非同步讀取檔案內容,注意這兒Fs的生命週期是有限的。
using (FileStream fs = new FileStream(_fileName, FileMode.Open,FileAccess.Read, FileShare.Read, 1024,FileOptions.Asynchronous))
{byte[] data = new byte[1024];
AsyncReadClass arc = new AsyncReadClass(data, fs);
fs.BeginRead(data, 0, 1024, FinishRead, arc);
//主線程執行一些其他動作
Thread.Sleep(1000 * 3);
Console.Read();}
private static void FinishRead(IAsyncResultar)
{
AsyncReadClass arc = ar.AsyncState as AsyncReadClass;
//讓非同步讀取佔用的資源釋放
int length = arc.Fs.EndRead(ar);//注意對象的生命週期}
*如何阻止線程執行內容的傳遞;
同一進程中線程雖然共用資源和記憶體塊,但仍然擁有自己的上線問,在.net中,線程的上下文有流動的特性;
線程執行內容的內容:安全上下文,調用上下文,同步上下文,本地化上下文,事務上下文,CLR宿主上下文;
內容相關的流動:Thread thread;thread.Start();thread.join()//阻塞當前線程;
如何阻止內容相關的流動:線程執行的上下文是所有線程的一個封裝,在通常情況下,當前線程的執行內容會流動到建立線程之中。程式員可以使用定義在System.Threading.ThreadPool類型的UnsafeQueueUserWorkItem方法和定義在ExecutionContext類型中的SuppressFlow方法來阻止這種流動。注意這樣雖然可以提高效率,但會降低安全性;
3.多線程程式的線程同步
*什麼是同步快和同步快索引;
.net團隊在設計基本架構時已經考慮了線程同步的問題,採用了折中的方式:為每個對記憶體對象分配一個索引,該索引中只存在一個表明數組內索引的整數。在.net載入時會建立一個同步塊數組,每當某個對象需要被同步時,.net會為其分配一個同步塊,並且把該同步塊在同步數組中的索引加入對象的同步塊索引中;
當一個線程試圖使用該對象進入同步時,會檢查該對象的同步索引,如果索引為負數則會在同步塊數組中尋找或者建立一個同步塊,並且把同步塊索引值放入該對象的同步塊索引中,如果不為負值,則找到該同步塊,並且檢查是否被其他線程使用,如果有進入等待狀態,如果沒用則申明使用該同步塊.
進入和退出同步:System.Threading.Monitor.Enter/Exit;
*C#中的lock關鍵字有何作用;
lock等價於Monitor.Entry/Exit;在通常情況下,lock一個私用引用成員變數來完成成員方法的線程同步,使用一個私人靜態引用變數來完成靜態方法的線程同步;
*是否可以使用實值型別對象來實現線程同步?不能,會出現嚴重錯誤,Monitor相關方法使用時會出現拆裝箱,每一次堆內的對象均會改變,出現嚴重錯誤。所以應該使用lock,而不要使用Monitor.Entrr/Exit;
*可否對參考型別自身進行同步;可以,但這樣的程式缺乏健壯性(lock(this),lock(Typeof(...))),當某個類型使用者惡意地長期佔用對象的同步塊時,所有的其他對象會死結;
*什麼是互斥體,Mutex類型,Monitor類型的功能有何區別;(WaitHandle(所有封裝的核心同步對象的的抽象基類),類似的還有Semaphone,EventWaitHandler);
Mutex.WaitOne();Mutex.Close();Mutex.ReleaseMutex();
Mutex使用OS核心對象,Monitor在.net架構實現,mutex效率低下(10倍,使用者態->系統態);
Monitor只能同步一個AppDomian中的線程,而Mutex可以跨越Process;
4.名企真題;
*進程與線程如何理解?作業系統中的進程擁有自己獨立的記憶體空間(包含資料區塊,程式塊),如Win32(分配4G的虛擬記憶體空間),進程可以被作業系統調度,簡單來說,一個進程代表了一個正在啟動並執行應用程式實體,可以包含一個或多個線程;線程是一個可以被調度的單元,維護自己獨立的堆棧和上下文環境;
進程與線程的最大區別是隔離性,每個進程獨立的運行,擁有自己的記憶體塊,獨佔資源,相互間的互動困難;而一個進程中的多個線程可以共用資料和記憶體塊,一個線程可以訪問,結束同一進程中另外的線程;
*根據安全執行緒的相關知識,分析當調用test時,i>10是否會引起死結:
public void test(int i){
lock(this){
if(i>10){
i--;
test(i);
}}}//首先不建議使用this之中lock方式;//不會死結,因為傳的是實值型別;(未OK)
*後台線程與一般線程有何區別:
前台線程能阻止應用程式的終結,一直到所有的前台線程終止後,CLR才能關閉應用程式。後台線程又被稱為守護線程,它被CLR認為是程式執行中可以做出犧牲的途徑,即任何時候都可以被忽略,因此,如果所有的前台線程終止,應用程式卸載時,所有的後台線程也會被自動終止.
*一共有幾種方法在多線程間共用資料?(這兒的共用其實就是指的如何同步)lock,mutex;
*使用lock和mutex的區別:效率上lock高很多,但mutex因為是os核心對象所以可以實現多進程間的同步;
*是否可以對實值型別使用lock?不能,CLR的機制是在所有的堆對象中分配一個同步塊索引,實值型別沒有;
*你會在什麼時候考慮使用多線程?比如考慮到相應速度,不希望因為資源的阻塞而影響使用者的使用;
*使用Thread類型建立的線程是否來自線程池?必須不是,ThreadPool.QueueUseWorkItem();
第三部分 .net單元測試
1.單元測試的基本概念;
*簡述單元測試概念和優點;
*舉例說明TDD開發方式流程;
*編程階乘功能模組測試案例;
2.使用NUnit進行單元測試;
*如何使用NUNIT進行單元測試;
*如何對測試案例進行分類;
*解釋SetUp,TearDown;
3.名企面試真題;
.NET工作準備--03進階知識