C#基礎-第5章:基元型別、引用型別和值型別
阿新 • • 發佈:2019-01-04
5 本章內容:
- 程式語言的基元型別
- 引用型別和值型別
- 值型別的裝箱和拆箱
- ·物件雜湊碼 (暫無程式碼演示)
- dynamic基元型別
PS:以下程式碼以裝箱和拆箱的說明居多
using System; using System.Collections; using System.Collections.Generic; using System.Reflection; using System.Dynamic; using System.Linq; using Microsoft.CSharp.RuntimeBinder; /////////////////////////////////////////////////////////////////////////////// publicstatic class Program { public static void Main() { PrimitiveDemo(); BoxingDemo(); ReferenceVsValue.Go(); Boxing.Go(); BoxingForInterfaceMethod.Go(); MutateViaInterface.Go(); DynamicDemo.Go(); } private static void PrimitiveDemo()//P99-100 { //以下4行都能正確的編譯,並生成相同的IL程式碼 int a = new int(); //不方便的語法 int b = 0; //最方便的語法 System.Int32 c = new System.Int32(); //最不方便的語法 Int32 d = 0; //方便的語法 Int32 int 是等價的 // 顯示全部變數為0 Console.WriteLine("a = {0}, b = {1}, c = {2}, d = {3}", new Object[] { a, b, c, d }); // 設定全部的值為5 a = b = c = d = 5; Console.WriteLine("a = {0}, b = {1}, c = {2}, d = {3}", new Object[] { a, b, c, d }); } private static void BoxingDemo() { Int32 a = 5; // 建立未裝箱的值型別變數 Object o = a; // o引用已裝箱的a的版本 a = 123; // 修改未裝箱的值 123 Console.WriteLine(a + ", " + (Int32)o); // 顯示 "123, 5" Console.WriteLine(a + ", " + o); // 優化 Console.WriteLine(a); // 沒有裝箱 } //引用型別vs值型別 private static class ReferenceVsValue { //引用型別 (因為 'class') private class SomeRef { public Int32 x; } // 值型別 (因為 'struct') private struct SomeVal { public Int32 x; } public static void Go()//P(107) { SomeRef r1 = new SomeRef(); // 在堆上分配 SomeVal v1 = new SomeVal(); // 在棧是分類 r1.x = 5; // 提領指標 v1.x = 5; // 在棧上修改 Console.WriteLine(r1.x); // 顯示 "5" Console.WriteLine(v1.x); // 同樣顯示 "5" //圖5-2的左半部分反映了執行以上程式碼之後的情況 SomeRef r2 = r1; // 只複製應用(指標) SomeVal v2 = v1; // 在棧上分配並複製成員 r1.x = 8; // r1.x 和 r2.x 都會改變 v1.x = 9; // v1.x 會變, v2.x 不變 Console.WriteLine(r1.x); // 顯示 "8" Console.WriteLine(r2.x); // 顯示 "8" Console.WriteLine(v1.x); // 顯示 "9" Console.WriteLine(v2.x); // 顯示 "5" //圖5-2的右半部分反映了執行以上所有程式碼之後的情況 } } private static class Boxing { public static void Go()//P(111) { ArrayList a = new ArrayList(); Point p; //分配一個Point(不在堆中分配). for (Int32 i = 0; i < 10; i++) { p.x = p.y = i; // 初始化值型別中的成員 a.Add(p); // 對值的型別裝箱,將引用新增到ArrayLis中 } } // 宣告一個值型別 private struct Point { public Int32 x, y; } public static void Main2()//P(113) { Int32 x = 5; Object o = x; // 對x裝箱,o引用已裝箱物件 Int16 y = (Int16)o; // 丟擲 InvalidCastException 異常 } public static void Main3()//P(113) { /* Main2 方法正確的轉化方式 */ Int32 x = 5; Object o = x; // 對x進行裝箱,o引用已裝箱物件 Int16 y = (Int16)(Int32)o; // 先拆箱為正確型別,再轉型 } public static void Main4() { Point p; p.x = p.y = 1; Object o = p; //對P進行裝箱 p = (Point)o; //對o進行拆箱,將欄位從已裝箱例項複製到棧變數中 } public static void Main5() { Point p; p.x = p.y = 1; Object o = p; // 對p進行裝箱;o引用已裝箱的例項 // 將 Point的欄位變成2 p = (Point)o; // 對o拆箱,將欄位從已裝箱的例項複製到棧變數中 p.x = 2; // 更改棧變數的狀態 o = p; // 對p裝箱;o引用新的已裝箱例項 } public static void Main6()//P(114) { Int32 v = 5; // 建立未裝箱值型別變數 Object o = v; // o引用已裝箱的,包含值5的Int32 v = 123; // 改變為裝箱的的值改成123 Console.WriteLine(v + ", " + (Int32)o); // 顯示 "123, 5" } public static void Main7()//P(116) { Int32 v = 5; // 建立未裝箱值型別變數. Object o = v; // o 引用v的已裝箱版本 v = 123; //將未裝箱的值型別修改成123 Console.WriteLine(v); // 顯示 "123" v = (Int32)o; //拆箱並將o複製到v Console.WriteLine(v); // 顯示 "5" } public static void Main8()//P(117) { Int32 v = 5; // 建立未裝箱的值型別變數 #if INEFFICIENT // 編譯下面這一行,v 被裝箱3次,浪費時間和記憶體 Console.WriteLine("{0}, {1}, {2}", v, v, v); #else // 下面的程式碼結果一樣,但無論執行速度, // 還是記憶體利用,都比前面的程式碼更勝一籌 Object o = v; // 對v手動裝箱(僅一次) // 編譯下面這一行不發生裝箱 Console.WriteLine("{0}, {1}, {2}", o, o, o); #endif } } private static class BoxingForInterfaceMethod { private struct Point : IComparable //p(118) { private Int32 m_x, m_y; //構造器負責初始化欄位 public Point(Int32 x, Int32 y) { m_x = x; m_y = y; } //重寫從System.ValueType 繼承的ToString方法 public override String ToString() { //如果呼叫了 base.ToString() 則會被裝箱 //將point做字串返回。注意:呼叫ToString以避免裝箱 return String.Format("({0}, {1})", m_x, m_y); } // 實現型別安全的CompareTo方法 public Int32 CompareTo(Point other) { // 利用勾股定理計算哪個point距離原點(0,0)更遠 return Math.Sign(Math.Sqrt(m_x * m_x + m_y * m_y) - Math.Sqrt(other.m_x * other.m_x + other.m_y * other.m_y)); } // 實現IComparable 的 CompareTo 方法 public Int32 CompareTo(Object o) { if (GetType() != o.GetType()) { throw new ArgumentException("o is not a Point"); } // 呼叫型別安全的ComareTo方法 return CompareTo((Point)o); } } public static void Go() //P(119) { // 在棧上建立2個Point的例項 Point p1 = new Point(10, 10); Point p2 = new Point(20, 20); // 呼叫ToString(虛方法)不裝箱p1; Console.WriteLine(p1.ToString()); // 顯示"(10, 10)" // 呼叫GetType(非虛方法)時,要對P1進行裝箱 Console.WriteLine(p1.GetType()); // 顯示"Point" //呼叫CompareTo 不裝箱p1 //由於呼叫的是CompareTo(Point),所以p2不裝箱 Console.WriteLine(p1.CompareTo(p2)); // "-1" // p1要裝箱,引用放到c中 IComparable c = p1; Console.WriteLine(c.GetType()); // 顯示"Point" //呼叫CompareTo不裝箱p1 //由於CompareTo傳遞不是Point變數 //所以呼叫的是CompareTo(Object),它要求獲取對裝箱Point的引用 //c不裝箱是因為本來就引用了已裝箱Point Console.WriteLine(p1.CompareTo(c)); // 顯示"0" // c不裝箱,因為它本來就引用了已裝箱Point // p2要裝箱,因為呼叫的是CompareTo(Object) Console.WriteLine(c.CompareTo(p2)); // "-1" //對c拆箱,欄位複雜到P2中 p2 = (Point)c; // 證明欄位已複製到P2中 Console.WriteLine(p2.ToString()); // "(10, 10)" } } private static class MutateViaInterface { // 介面定義了Change方法 private interface IChangeBoxedPoint { void Change(Int32 x, Int32 y); } // Point是值型別. private struct Point : IChangeBoxedPoint { private Int32 m_x, m_y; public Point(Int32 x, Int32 y) { m_x = x; m_y = y; } public void Change(Int32 x, Int32 y) { m_x = x; m_y = y; } public override String ToString() { return String.Format("({0}, {1})", m_x, m_y); } } public static void Go() { Point p = new Point(1, 1); Console.WriteLine(p); p.Change(2, 2); Console.WriteLine(p); Object o = p; Console.WriteLine(o); ((Point)o).Change(3, 3); Console.WriteLine(o); // 對p進行裝箱,更改已裝箱的物件,然後丟棄它 ((IChangeBoxedPoint)p).Change(4, 4); Console.WriteLine(p); // 更改已裝箱的的對戲,並顯示它 ((IChangeBoxedPoint)o).Change(5, 5); Console.WriteLine(o); /********************* 如果把Point 改成class 引用型別 就不存在裝箱,拆箱的的轉化,上面的結果就會發變化 ********************/ } } private static class DynamicDemo { public static void Go() { ShowLoadedAssemblies("Assemblies loaded before use of dynamic"); SimpleDynamic(); ShowLoadedAssemblies("Assemblies loaded after simkple use of dynamic"); Demo(); ShowLoadedAssemblies("Assemblies loaded after all dynamic code runs"); ExcelAutomation(); DynamicStaticInvocations(); } private static void ShowLoadedAssemblies(String caption) { Console.WriteLine(caption); foreach (Assembly a in AppDomain.CurrentDomain.GetAssemblies()) Console.WriteLine(" " + a.GetName().Name); Console.WriteLine(); } private static Int32 SimpleDynamic() { return ((dynamic)0) + 0; } private static void Demo() //P(129) { dynamic value; for (Int32 demo = 0; demo < 2; demo++) { value = (demo == 0) ? (dynamic)5 : (dynamic)"A"; value = value + value; M(value); } Object o = 123; // OK: 從Int32 隱形轉型為Object(裝箱) //Int32 n1 = o; // Error: 不允許從Object到Int32的隱式轉化 Int32 n2 = (Int32)o; // OK: 從Object顯示轉型為Int32(拆箱) dynamic d = 123; // OK: 從 Int32 隱形轉化為 dynamic(裝箱) Int32 n3 = d; // OK: 從 dynamic 隱式轉為 Int32(拆箱) try { var m = M(d); // 注意: 'var m' 等同於 'dynamic m' } catch (RuntimeBinderException) { } var x = (Int32)d; // 'var x' 等同於 'Int32 x' var dt = new DateTime(d); // 'vat dt' 等同於 'DateTime dt' } private static void M(Int32 n) { Console.WriteLine("M(Int32): " + n); } private static void M(String s) { Console.WriteLine("M(String): " + s); } /// <summary> /// 構造一個 'dynamic' 的例項派生類,來動態呼叫型別的靜態成員 /// </summary> internal sealed class StaticMemberDynamicWrapper : DynamicObject //P(132) { private readonly TypeInfo m_type; public StaticMemberDynamicWrapper(Type type) { m_type = type.GetTypeInfo(); } public override IEnumerable<String> GetDynamicMemberNames() { return m_type.DeclaredMembers.Select(mi => mi.Name); } public override bool TryGetMember(GetMemberBinder binder, out object result) { result = null; var field = FindField(binder.Name); if (field != null) { result = field.GetValue(null); return true; } var prop = FindProperty(binder.Name, true); if (prop != null) { result = prop.GetValue(null, null); return true; } return false; } public override bool TrySetMember(SetMemberBinder binder, object value) { var field = FindField(binder.Name); if (field != null) { field.SetValue(null, value); return true; } var prop = FindProperty(binder.Name, false); if (prop != null) { prop.SetValue(null, value, null); return true; } return false; } public override Boolean TryInvokeMember(InvokeMemberBinder binder, Object[] args, out Object result) { MethodInfo method = FindMethod(binder.Name, args.Select(a => a.GetType()).ToArray()); if (method == null) { result = null; return false; } result = method.Invoke(null, args); return true; } private MethodInfo FindMethod(String name, Type[] paramTypes) { return m_type.DeclaredMethods.FirstOrDefault(mi => mi.IsPublic && mi.IsStatic && mi.Name == name && ParametersMatch(mi.GetParameters(), paramTypes)); } private Boolean ParametersMatch(ParameterInfo[] parameters, Type[] paramTypes) { if (parameters.Length != paramTypes.Length) return false; for (Int32 i = 0; i < parameters.Length; i++) if (parameters[i].ParameterType != paramTypes[i]) return false; return true; } private FieldInfo FindField(String name) { return m_type.DeclaredFields.FirstOrDefault(fi => fi.IsPublic && fi.IsStatic && fi.Name == name); } private PropertyInfo FindProperty(String name, Boolean get) { if (get) return m_type.DeclaredProperties.FirstOrDefault( pi => pi.Name == name && pi.GetMethod != null && pi.GetMethod.IsPublic && pi.GetMethod.IsStatic); return m_type.DeclaredProperties.FirstOrDefault( pi => pi.Name == name && pi.SetMethod != null && pi.SetMethod.IsPublic && pi.SetMethod.IsStatic); } } private static class StaticTestType { public static String Method(Int32 x) { return x.ToString(); } #pragma warning disable 649 // 忽略警告 欄位永遠不會被賦值給,並且總是有它的預設值 public static DateTime Field; #pragma warning restore 649 public static Guid Property { get; set; } } private static void DynamicStaticInvocations() { dynamic staticType = new StaticMemberDynamicWrapper(typeof(String)); Console.WriteLine(staticType.Concat("A", "B")); //動態呼叫String 的靜態方法 staticType = new StaticMemberDynamicWrapper(typeof(StaticTestType)); Console.WriteLine(staticType.Method(5)); staticType.Field = DateTime.Now; Console.WriteLine(staticType.Field); staticType.Property = Guid.NewGuid(); Console.WriteLine(staticType.Property); } } private static void ExcelAutomation() { #if ReferencingExcel // Microsoft.Office.Interop.Excel.dll var excel = new Microsoft.Office.Interop.Excel.Application(); excel.Visible = true; excel.Workbooks.Add(Type.Missing); ((Range)excel.Cells[1, 1]).Value = "Text in cell A1"; // Put a string in cell A1 excel.Cells[1, 1].Value = "Text in cell A1"; // Put a string in cell A1 #endif } } //////////////////////////////// End of File //////////////////////////////////