C#面試題(二)(包含答案) ------ GC/反射泛型
1.GC 機制
垃圾收集,Garbage Collector(垃圾收集器,在不至於混淆的情況下也成為GC)以應用程式的root為基礎,遍歷應用程式在Heap上動態分配的所有物件,通過識別它們是否被引用來確定哪些物件是已經死亡的、哪些仍需要被使用。已經不再被應用程式的root或者別的物件所引用的物件就是已經死亡的物件,即所謂的垃圾,需要被回收。 比較常見的演算法有Reference Counting,Mark Sweep,Copy Collection等等首先,GC並不是能釋放所有的資源。它不能自動釋放非託管資源。
第二,GC並不是實時性的,這將會造成系統性能上的瓶頸和不確定性。
託管資源指的是.NET可以自動進行回收的資源,主要是指託管堆上分配的記憶體資源。
非託管資源指的是.NET不知道如何回收的資源,最常見的一類非託管資源是包裝作業系統資源的物件,例如檔案,視窗,網路連線,資料庫連線,畫刷,圖示等。這類資源,垃圾回收器在清理的時候會呼叫Object.Finalize()方法。預設情況下,方法是空的,對於非託管物件,需要在此方法中編寫回收非託管資源的程式碼,以便垃圾回收器正確回收資源。
在.NET中,Object.Finalize()方法是無法過載的,編譯器是根據類的解構函式來自動生成Object.Finalize()方法的,所以對於包含非託管資源的類,可以將釋放非託管資源的程式碼放在解構函式。
GC並不是實時性的,這會造成系統性能上的瓶頸和不確定性。所以有了IDisposable介面,IDisposable介面定義了Dispose方法,這個方法用來供程式設計師顯式呼叫以釋放非託管資源。使用using語句可以簡化資源管理。
public class Foo : IDisposable { private bool disposed = false; //既釋放託管資源,又釋放非託管資源 public void Dispose() { Dispose(true); //將物件從垃圾回收器連結串列中移除, //從而在垃圾回收器工作時,只回收託管資源,而不執行物件的解構函式 GC.SuppressFinalize(this); } protected virtual void Dispose(bool disposing) { if (!disposed) { if (disposing) { //釋放託管資源 } // 釋放非託管資源 disposed = true; } } //解構函式不是人工呼叫,由垃圾回收器呼叫,用於釋放非託管資源 ~Foo() { Dispose(false); } }
using (Student stu = new Student() { StuID = 2 })
{
Console.WriteLine("釋放之前》");
Console.WriteLine(stu == null);
}
using 關鍵字保證在離開 using 作用域的時候一定會呼叫 Dispose 。所以,如果只是在區域性使用了 IDisposable 變數,最好這樣包起來,以防止記憶體洩漏。
System.GC 下的方法:
GC.SuppressFinalize(this); //請求公共語言執行時不要呼叫指定物件的終結器。呼叫Dispose方法,銷燬了物件,而GC並不知道,這個方法就是告訴GC,不需要在呼叫這些物件的Finalize()方法了,
GC.GetTotalMemory(false); //檢索當前認為要分配的位元組數。 一個引數,指示此方法是否可以等待較短間隔再返回,以便系統回收垃圾和終結物件。
GC.Collect(); //強制對所有代進行即時垃圾回收
GC.AddMemoryPressure(); //通知執行時在安排垃圾回收時應考慮分配大量的非託管記憶體。
GC.CancelFullGCNotification(); //取消註冊垃圾回收通知。
GC.CollectionCount(); //返回已經對物件的指定代進行的垃圾回收次數。
GC.EndNoGCRegion(); //結束無 GC 區域延遲模式。
GC.GetGeneration(); //返回物件的當前代數。
GC.GetTotalMemory(); //檢索當前認為的要分配的位元組數
GC.KeepAlive(); //引用指定物件,使其從當前例程開始到呼叫此方法的那一刻為止均不符合進行垃圾回收的條件。
GC.RegisterForFullGCNotification (); //指定當條件支援完整垃圾回收以及回收完成時,應引發垃圾回收通知。
GC.RemoveMemoryPressure(); //通知執行時已釋放非託管記憶體,在安排垃圾回收時不需要再考慮它。
GC.ReRegisterForFinalize(); //請求系統呼叫指定物件的終結器,此前已為該物件呼叫 SuppressFinalize。
GC.TryStartNoGCRegion(); //在關鍵路徑執行期間嘗試禁止垃圾回收。
GC.WaitForFullGCApproach(); //返回已註冊通知的狀態,用於確定公共語言執行時是否即將引發完整、阻礙性垃圾回收。
GC.WaitForFullGCComplete (); //返回已註冊通知的狀態,用於確定公共語言執行時引發的完整、阻礙性垃圾回收是否已完成。
GC.WaitForPendingFinalizers (); //掛起當前執行緒,直到處理終結器佇列的執行緒清空該佇列為止。
2.CLR IL 等概念
1.Net平臺上各種高階語言,如c#、VB、F#等編寫的程式碼,2.首先會通過各自的直譯器,解釋成(MS)IL(Intermediate Language)(微軟)(中間語言)組成的位元組碼,
3.最後通過CLR(Common Language Runtime)(公共語言執行時)特定的JIT(實時編譯器)編譯成機器碼。
3.反射 / 泛型
首先來了解type類:
Type類的屬性:
Name 資料型別名
FullName 資料型別的完全限定名(包括名稱空間名)
Namespace 定義資料型別的名稱空間名
IsAbstract 指示該型別是否是抽象型別
IsArray 指示該型別是否是陣列
IsClass 指示該型別是否是類
IsEnum 指示該型別是否是列舉
IsInterface 指示該型別是否是介面
IsPublic 指示該型別是否是公有的
IsSealed 指示該型別是否是密封類
IsValueType 指示該型別是否是值型別
Type類的方法:
GetConstructor(), GetConstructors():返回ConstructorInfo型別,用於取得該類的建構函式的資訊
GetEvent(), GetEvents():返回EventInfo型別,用於取得該類的事件的資訊
GetField(), GetFields():返回FieldInfo型別,用於取得該類的欄位(成員變數)的資訊
GetInterface(), GetInterfaces():返回InterfaceInfo型別,用於取得該類實現的介面的資訊
GetMember(), GetMembers():返回MemberInfo型別,用於取得該類的所有成員的資訊
GetMethod(), GetMethods():返回MethodInfo型別,用於取得該類的方法的資訊
GetProperty(), GetProperties():返回PropertyInfo型別,用於取得該類的屬性的資訊
可以呼叫這些成員,其方式是呼叫Type的InvokeMember()方法,或者呼叫MethodInfo, PropertyInfo和其他類的Invoke()方法。
反射的用途:
(1)使用Assembly定義和載入程式集,載入在程式集清單中列出模組,以及從此程式集中查詢型別並建立該型別的例項。
//通過程式集的名稱反射
Assembly ass = Assembly.Load("ClassLibrary831");
Type t = ass.GetType("ClassLibrary831.NewClass");
object o = Activator.CreateInstance(t, "grayworm", "http://hi.baidu.com/grayworm");
MethodInfo mi = t.GetMethod("show");
mi.Invoke(o, null);
//通過DLL檔案全名反射其中的所有型別
Assembly assembly = Assembly.LoadFrom("xxx.dll的路徑");
Type[] aa = a.GetTypes();
foreach(Type t in aa)
{
if(t.FullName == "a.b.c")
{
object o = Activator.CreateInstance(t);
}
}
(2)使用Module瞭解包含模組的程式集以及模組中的類等,還可以獲取在模組上定義的所有全域性方法或其他特定的非全域性方法。 (3)使用ConstructorInfo瞭解建構函式的名稱、引數、訪問修飾符(如pulic 或private)和實現詳細資訊(如abstract或virtual)等。
Type t = T.GetType();
ConstructorInfo[] ci = t.GetConstructors(); //獲取類的所有建構函式,也可以傳GetProperties引數
foreach (ConstructorInfo c in ci) //遍歷每一個建構函式
{
ParameterInfo[] ps = c.GetParameters(); //取出每個建構函式的所有引數
foreach (ParameterInfo pi in ps) //遍歷並列印所該建構函式的所有引數
{
Console.Write(pi.ParameterType.ToString()+" "+pi.Name+",");
}
Console.WriteLine();
}
GetProperties(BindingFlags)說明:
Instance|Public:獲取公共的的例項屬性(非靜態的)
Instance|NonPublic:獲取非公共的的例項屬性(非靜態的)。(private/protect/internal)
Static|Public:獲取公共的靜態屬性
Static|NonPublic:獲取非公共的靜態屬性。(private/protect/internal)
Instance|Static|Public:獲取公共的的例項或靜態屬性
Instance|Static|NonPublic:非獲取公共的的例項或靜態屬性
用建構函式動態生成物件:
Type t = typeof(NewClassw);
Type[] pt = new Type[2];
pt[0] = typeof(string);
pt[1] = typeof(string);
//根據引數型別獲取建構函式
ConstructorInfo ci = t.GetConstructor(pt);
//構造Object陣列,作為建構函式的輸入引數
object[] obj = new object[2]{"grayworm","hi.baidu.com/grayworm"};
//呼叫建構函式生成物件
object o = ci.Invoke(obj);
//呼叫生成的物件的方法測試是否物件生成成功
//((NewClassw)o).show();
用Activator生成物件,其中方法返回的物件就是對應的例項引用:
Type t = typeof(NewClassw);
//建構函式的引數
object[] obj = new object[2] { "grayworm", "hi.baidu.com/grayworm" };
//用Activator的CreateInstance靜態方法,生成新物件
object o = Activator.CreateInstance(t,"grayworm","hi.baidu.com/grayworm");
(4)使用MethodInfo瞭解方法的名稱、返回型別、引數、訪問修飾符(如pulic 或private)和實現詳細資訊(如abstract或virtual)等。
Type t = T.GetType();
MethodInfo[] mis = t.GetMethods();
foreach (MethodInfo mi in mis)
{
MethodInfo mi1 = t.GetMethod(mi.Name, BindingFlags.NonPublic | BindingFlags.Instance);
MethodInfo mi2 = mi.MakeGenericMethod(new Type[] { typeof(string) });
mi2.Invoke(p, new object[] { "abcdefg" });
Console.WriteLine(mi.ReturnType+" "+mi.Name);
}
(5)使用FiedInfo瞭解欄位的名稱、訪問修飾符(如public或private)和實現詳細資訊(如static)等,並獲取或設定欄位值。
Type t = nc.GetType();
FieldInfo[] fis = t.GetFields();
foreach (FieldInfo fi in fis)
{
Console.WriteLine(fi.Name);
}
(6)使用EventInfo瞭解事件的名稱、事件處理程式資料型別、自定義屬性、宣告型別和反射型別等,新增或移除事件處理程式。
Type t = nc.GetType();
EventInfo[] events = t .GetEvents();
// 輸出事件資訊
foreach (EventInfo ev in events)
{
Console.WriteLine(" 事件名:{0},事件型別:{1}", ev.Name, ev.EventHandlerType.Name);
}
(7)使用PropertyInfo瞭解屬性的名稱、資料型別、宣告型別、反射型別和只讀或可寫狀態等,獲取或設定屬性值。
Type t = nc.GetType();
PropertyInfo[] pis = t.GetProperties();
foreach(PropertyInfo pi in pis)
{
Console.WriteLine(pi.Name);
Debug.log (T.GetType().GetProperty(p.Name).GetValue(T, null))
}
如果要訪問引用變數,可以使用特定的名字來訪問:
var list = T.GetType().GetProperty("dictionaryName").GetValue(T, null);
foreach (object o in (list as IEnumerable))
{
var itemKey = o.GetType().GetProperty("Key").GetValue(o, null);
Console.WriteLine(itemKey);
var itemValue = o.GetType().GetProperty("Value").GetValue(o, null);
foreach (object subItem in (itemValue as IEnumerable))
Console.WriteLine(subItem);
}
(8)使用ParameterInfo瞭解引數的名稱、資料型別、是輸入引數還是輸出引數,以及引數在方法簽名中的位置等。
Type t = nc.GetType();
MethodInfo[] mis = t.GetMethods();
foreach (MethodInfo mi in mis)
{
ParameterInfo[] ps = mi.GetParameters(); //取出每個建構函式的所有引數
foreach (ParameterInfo pi in ps) //遍歷並列印所該建構函式的所有引數
{
Console.Write(pi.ParameterType.ToString()+" "+pi.Name+",");
}
}