C# 全总结
C# 全總結
1 using System; 2 using System.Collections.Generic; 3 //using System.Linq; 4 using System.Text; 5 using System.Diagnostics; 6 using System.IO; 7 using static System.Console; 8 using System.Linq; 9 using System.Runtime.InteropServices; 10 using System.Threading; 11 12 namespace ConsoleApplication1 13 { 14 class Program 15 { 16 [DllImport("dltest.dll", EntryPoint ="Print")] 17 static extern void xPrint(int x); 18 #region old-test 19 20 // 21 static void TestStreamReadWrite() 22 {//一個流不能兼備讀寫兩種操作,不知道為什么,這不合理 23 string s1 = "你好啊ABC"; 24 25 var t = "你好啊ABC".Length; 26 //關于編碼注意幾點: 27 //1,sizeof(char) 等于 2 28 //2,str.Length 是以元素個數算的,不是按字節算的,如 "你好啊ABC” length = 6 29 //3,c#在VS的默認編碼為 Encoding.Default, 該編碼下漢字占兩字節,非漢字占1字節,通過查看ms中的字節數據可知 30 //4,string 類型寫入任何buffer時都是先寫長度,一般為1字節,再寫字節數據,如下 31 var sz = sizeof(char); //2, 注意char占2字節 32 var szb = sizeof(bool); //1 33 34 var ms = new MemoryStream(); 35 var writer = new BinaryWriter(ms, Encoding.UTF7); 36 writer.Write(s1); 37 ms.Close(); 38 writer.Close(); 39 40 var ms2 = new MemoryStream(ms.GetBuffer()); 41 var reader = new BinaryReader(ms2, Encoding.UTF8); 42 var s2 = reader.ReadString(); 43 44 } 45 46 // 47 static void TestEncoding() 48 { 49 string s1 = "你好啊ABC"; 50 51 //漢字亂碼問題,漢字必須使用2個以上字節才能表示 52 //編碼方式 53 //1,ASCII碼,只有一個字節,不能正確表示漢字,出現亂碼,可以正確表示數字和字母符號 54 //2,UNICODE,任何符號都用2個字節表示,因此可以表示漢字和任意符號 55 //3,UTF8,變字節的編碼,可以正確表示任何字符和漢字,各國語言 56 //4,GB2312編碼,國標碼,主要是為漢字服務的中國編碼,漢字占兩字節,字母數字占1字節 57 //5,default編碼,在國內, 般就是GB2312 58 Encoding.Default.GetBytes(s1); 59 var bytes = Encoding.GetEncoding("GB2312").GetBytes(s1); 60 var len = bytes.Length; 61 var bts = new byte[10 + len]; 62 Array.ConstrainedCopy(bytes, 0, bts, 0, len); 63 64 var s2 = Encoding.GetEncoding("GB2312").GetString(bts).TrimEnd('\0'); 65 string s3 = "\0hello/0/0dddddd".TrimStart('\0');//!!!!!!!!!!!!!!!!!!!!!!!!!!!! 66 67 } 68 69 #region 計算機中數據的存儲 70 // 71 static void TestTypeConvert() 72 {//把一個有符號數轉為無符號后再轉回來值保持不變,以下以1字節為例 73 //原理:計算機中符點數都是有符號的,不存在這種轉變,只剩下整數, 74 //真值:絕對值的二進制值,如-1的真值為 00000001 75 //整數是以補碼形式存放的,計算機規定了正數的補碼是本身,負數的補碼是:符號位不變,真值按位取反再加1 76 //強制轉換做的事就是把一個補碼看成是有符號還是無符號 77 //有符號數,在計算時:符號位不變,真值按位取反再加1。無符號數直接計算,舉例如下: 78 //1,-1 的真值為00000001,補碼為 1 111 1111,強轉時就是把補碼值看作是一個無符數,因此它=255 79 //,再次強轉時把它看成有符號數,符號位不管,其余位按位取反加1后是1,因此再次轉回了-1 80 //2,-2 的真值為00000010,補碼為 1 111 1110,強轉時把補碼看作無符號數,因此它=254 81 //3,-128真值有點特殊,128的二進制碼為1000 0000,第8位是符號位,舍棄,取后面的0,即-128的真值為0 82 //補碼經按位取反加1后還是 1 000 0000,強轉時看成無符號數即為128 83 //------------------------------------------- 84 //1字節數據和2字節數據進行加法運算時,要進行位擴展,將1字節擴展為2字節 85 //正數擴展時高位補0,負數擴展時高位補1 86 //C#中小于4字節的數據進行運算時會先擴展成int再進行 87 sbyte sb = -127; 88 var b = (byte)(sb); 89 var sb1 = (sbyte)(b); 90 object dx = 10.0f; 91 double dx2 = 33; 92 byte ix = (byte)dx2; 93 94 var t = dx.GetType(); 95 Type T = System.Type.GetType(t.FullName, true); 96 97 98 } 99 #endregion 100 101 // 102 void TestUncheck() 103 { 104 unchecked 105 {//不被編譯系統做編譯時安全檢查 106 107 } 108 } 109 110 static void TestBoxing() 111 { 112 int i = 10; 113 object o = 1; 114 int i2 = (int)o; 115 } 116 117 static void TestReadBytes() 118 { 119 byte[] bts = new byte[4] { 23, 0, 16, 0 }; 120 var ms = new MemoryStream(bts); 121 var br = new BinaryReader(ms); 122 var p1 = ms.Position; 123 var ix = br.ReadUInt32(); 124 var p2 = ms.Position; 125 Console.WriteLine("num=" + ix); 126 br.Dispose(); 127 br.Close(); 128 ms.Dispose(); 129 ms.Close(); 130 } 131 132 static void TestStrEnd() 133 { 134 string str = "abcde\0"; 135 var br = new BinaryReader(new MemoryStream(Encoding.ASCII.GetBytes(str))); 136 var b = br.ReadByte(); 137 while (b != 0) 138 { 139 Console.WriteLine(b); 140 try 141 { 142 b = br.ReadByte(); 143 } 144 catch (System.Exception ex) 145 { 146 Console.WriteLine("未發現字符串結束符"); 147 break; 148 } 149 } 150 } 151 152 static void TestBigEndia() 153 { 154 var br = new BinaryWriter(File.Create("f:/testx.dt"), Encoding.ASCII); 155 br.Write((Int16)9); 156 string str = "Stand"; 157 br.Write(str); 158 br.Write((Int16)10); 159 br.Write((Int16)70); 160 br.Dispose(); 161 162 } 163 164 static void TestChar0() 165 {//注意字符串中0和\0的區別,如 s1="h0ello", s2 = "h\0ello" 166 //s2中的\0是字符串結尾符,除了C#不把它作為結束符外,其它語言都把它作為結束符,如U3D,LUA,C/C++等 167 //而s1中的0僅是一個字符0而已,字符0的ASCII值是0X31=49,'\0'的ASCII值是0 168 //注意這兩種0在C#和U3D的API之間切換時容易造成BUG,如: 169 //1, debug.log(s1): "h0ello" 170 //2,debug.log(s2): "h" 171 var s = "hello"; 172 s += 0 + ",world"; 173 var s1 = "hello"; 174 s1 += (char)0 + ",world"; 175 var s2 = "hello"; 176 s2 += '\0' + ",world"; 177 } 178 static void MemTest() 179 { 180 181 } 182 static void ReflectionTest() 183 {//測試兩種反射的效率問題 184 //Type.GetType()只能在同一個程序集中使用,typeof則可以跨程序集(assembly) 185 //通過下面的實測,發現typeof是比GetType快40多倍 186 var timer = Stopwatch.StartNew(); 187 timer.Start(); 188 189 Type tx = Type.GetType("string"); 190 var tx1 = Type.GetType("float"); 191 timer.Stop(); 192 193 Console.WriteLine("T1= " + timer.Elapsed);//0.0000471 194 195 timer.Restart(); 196 197 tx = typeof(string); 198 tx1 = typeof(float); 199 200 timer.Stop(); 201 Console.WriteLine("T2= " + timer.Elapsed);//0.0000011 202 } 203 204 static void TestDelegate() 205 { 206 207 //類C++11風格:指定初始化容量20,使用初始化列表給部分成員賦值 208 var lst = new List<float>(20) { 1, 3, 4, 20, -2, 9, 0 }; 209 for (var i = 0; i < lst.Count; ++i) 210 { 211 //使用下標進行隨機訪問,說明list不是一個真正的鏈表,而是類似STL的Vector 212 Console.WriteLine(lst[i]); 213 } 214 215 //public void Sort (Comparison<T> comparison) 216 //public delegate int Comparison<T>(T x, T y); 217 218 219 //這是對調用List<int>.Sort進行排序的寫法,其中sort的定義及Comparison委托的定義如上 220 lst.Sort(new Comparison<float>(delegate (float m1, float m2) //委托 221 { 222 return 1; 223 })); 224 lst.Sort(delegate (float m1, float m2) //委托 225 { 226 return 1; 227 }); 228 lst.Sort((float m1, float m2) =>//Linq表達式 229 { 230 return 1; 231 }); 232 lst.Sort((m1, m2) => //Linq表達式 233 { 234 return 1; 235 }); 236 237 } 238 239 static string TestRetStr() 240 {//測試返回字符串是否會復制 241 return "helloworld"; 242 } 243 244 static void TestStrRet() 245 {//h1 = h2 = h3說明它們返回的是同一個字符串的引用 246 var s1 = TestRetStr(); 247 var s2 = TestRetStr(); 248 var s3 = TestRetStr(); 249 var h1 = s1.GetHashCode(); 250 var h2 = s1.GetHashCode(); 251 var h3 = s1.GetHashCode(); 252 } 253 static void TestVirtualFuncCall() 254 { 255 var otx = new CTestChildX(); 256 257 otx.Update();//輸出結果:child,如果注釋1處函數不加override,輸出結果為:base 258 var oty = new CTestY(); 259 oty.Update(); 260 oty.OnUpdate(); 261 262 } 263 static void TestStrModify() 264 { 265 var s1 = "hello"; 266 var s2 = s1; 267 s1 += "world"; 268 Console.WriteLine(s2); 269 270 var uns1 = s2.GetHashCode(); 271 Console.WriteLine(uns1); 272 } 273 274 static void Tests1() 275 { 276 var s1 = "hello"; 277 var uns1 = s1.GetHashCode(); 278 Console.WriteLine(uns1); 279 280 } 281 282 #endregion 283 284 #region 2018.3.30 285 #region ref out and template 286 class myTemp<T1, T2>//類入口 287 { 288 public T1 Add(T1 a, T1 b) 289 {//模板類型不能直接相加,必須先轉為動態類型,避開編譯檢查,運行時動態決定類型 290 dynamic da = a; 291 dynamic db = b; 292 return da + db; 293 } 294 295 public void tint<T3>()//注意C++不能這么寫,所有模板參數必須由類入口傳入 296 { 297 Type t = typeof(T3); 298 WriteLine(t); 299 } 300 } 301 302 delegate void refOutFunc(ref double t1, out double t2); 303 delegate T TemplateDelegate<T, U>(T a, U b); 304 static void TestRefAndOut() 305 { 306 //ref, out 本質上都是引用 307 //fef就為了傳給函數使用,必須先初始化,但也可以傳出數據,out是為了從函數中傳出數據使用,不用初始化 308 refOutFunc rof = delegate (ref double ax, out double bx) { 309 ax = 1; bx = 2;//ref out兩種類型的變量都被更改了 310 }; 311 312 double x1 = 0, x2; 313 rof(ref x1, out x2); 314 } 315 static void TestTemplate() 316 { 317 var otp = new myTemp<int, int>(); 318 otp.tint<object>(); 319 } 320 static T TempFunc<T, U>(T a, U b) 321 { 322 return a; 323 } 324 static void TestBufAligin() 325 {//自定義字節BUF的對齊測試 326 int x = 9; 327 int y = (x + 7) & ~7; 328 WriteLine(y); 329 } 330 #endregion 331 332 #endregion 333 334 #region 2018.4.9 335 336 //BUG?????? 337 //使用StopWatch測試運行時間 338 //兩段測試A和B 339 //測試結果受測試順序影響,后測要比先測耗時長了許多 340 341 static void TestKeyIntStr() 342 {// 343 var idict = new Dictionary<int, string>(); 344 var sdict = new Dictionary<string, string>(); 345 346 for (int i = 0; i < 1000000; i++) 347 { 348 var key = i * 2 + 1; 349 var v = i * i + ""; 350 idict.Add(key, v); 351 sdict.Add(key + "", v); 352 } 353 354 //測試 A 355 var t1 = 100000 * Test1(idict); 356 357 //測試 B 358 var t2 = 100000 * Test2(sdict); 359 360 Console.WriteLine("t1: {0},t2: {1}", t1, t2); 361 //Console.WriteLine("dt1: {0},dt2: {1}", dt1, dt2); 362 } 363 static float Test1(Dictionary<int, string> dict) 364 { 365 var timer = new Stopwatch(); 366 timer.Start(); 367 var it = dict[2001]; 368 var t1 = timer.ElapsedTicks; 369 timer.Stop(); 370 return (float)((float)t1 / Stopwatch.Frequency); 371 } 372 373 static double Test2(Dictionary<string, string> dict) 374 { 375 var timer = new Stopwatch(); 376 timer.Start(); 377 var it = dict["2001"]; 378 var t1 = timer.ElapsedTicks; 379 timer.Stop(); 380 return (float)((float)t1 / Stopwatch.Frequency); 381 } 382 #endregion 383 384 #region 2018.7.7 385 #region 數組的數組,二維數組 386 static int[] returnArray() 387 { 388 //數組是引用類型,分配在堆上 389 int[] arr = { 1, 2, 3, 4 }; //雖然這樣寫,其實等價于int[] arr = new int[]{1,2,3,4}; 390 return arr; //返回一個數組對象 391 } 392 static void TestArray() { 393 394 //1,一維數組 395 char[] arr = new char[2] { 'a', 'b' }; //必須全部初始化,或不初始化 396 int[] iarr = new int[2] { 0, 1 }; 397 char[] sarr = new char[3]; 398 399 //2,數組的數組,鋸齒數組 400 char[][] d2arr = new char[2][]; 401 d2arr[0] = new char[30]; 402 d2arr[1] = new char[2] { 'a', 'b' }; 403 d2arr[0][1] = 'x'; 404 405 //3,二維數組,矩陣 406 int[,] i2arr = new int[2, 3]; 407 for (var i = 0; i < 2; ++i) 408 { 409 for (var j = 0; j < 3; ++j) 410 { 411 i2arr[i, j] = i * 3 + j; 412 } 413 } 414 } 415 #endregion 416 #region 字段初始化無法使用非靜態(字段、方法、屬性) 417 delegate int mydelegate(int x); 418 //------------------------------------------------------------------------- 419 //字段初始化無法使用非靜態(字段、方法、屬性) 420 //------------------------------------------------------------------------- 421 float fxs; 422 static float sfxs; 423 //float fxs2 = fxs; //error 424 float fxs3 = sfxs; //right,可用靜態字段初始化 425 float fxs4 = TestStaticInit(); //right,調用靜態函數初始化 426 static int TestStaticInit() { return 10; } 427 mydelegate _mydel = (x) =>//LINQ為什么可以?,從下面可知,LINQ語句只相當于一堆初始化語句的集合 428 { 429 //int fx = fxs; //error 430 return 20; 431 }; 432 433 #endregion 434 #region 默認訪問修飾符 435 //1,名字空間中,最外層類及接口的默認修飾符為internal,也就是本程序集可訪問 436 //2,類中,變量,成員,類中類的默認修飾符為private 437 //3,結構中,同類 438 //4,接口中,所有方法和屬性都為public,接口中只能有方法,不能有變量 439 interface IMyinterface 440 {//接口中可以有方法,抽象屬性,不可以有變量 441 int Id { get; } //抽象屬性,公有 442 void Fly(); //方法,公有 443 } 444 #endregion 445 #region 類模板繼承 446 class CTClass<t1, t2, t3> //多個where的寫法 447 where t1 : struct //必須是值類型 448 where t2 : class //必須是引用類型 449 where t3 : new() //必須有無參構造函數 450 { 451 float fx, fy; 452 public static t1 Add(t1 a, t1 b) 453 { 454 return (dynamic)a + (dynamic)b; 455 } 456 } 457 458 //模板繼承的幾種方式 459 //1,全特化 460 class CDTClass : CTClass<int, CCmpBase, CCmpBase> { } 461 462 //2,原樣繼承,注意基類的所有約束都要重寫一遍 463 class CDTX<t1, t2, t3, t4> : CTClass<t1, t2, t3> 464 where t1 : struct //必須是值類型 465 where t2 : class //必須是引用類型 466 where t3 : new() //必須有無參構造函數 467 { } 468 //3,偏特化,介于二者之間的形態 469 #endregion 470 #region 運算符重載 471 class CCmpBase 472 {//帶有默認構造函數 473 float _x; 474 } 475 class CComplex : CCmpBase 476 { 477 float real, image; 478 public CComplex(float real, float image = 0) 479 { 480 this.real = real; 481 this.image = image; 482 } 483 484 //一,類型轉換 :數值轉對象 485 //CComplex cp = 2.1f 或 CComplex cp; cp = 2.1f; 486 //C#從不調用類型轉換構造函數進行類型轉換 487 public static implicit operator CComplex(float real) 488 { 489 return new CComplex(real); 490 } 491 492 //二,類型轉換:對象轉bool 493 public static explicit operator bool(CComplex cp) 494 { 495 return cp.real != 0 && cp.image != 0; 496 } 497 498 //三,類型轉換:對象轉數值 499 public static implicit operator float(CComplex cp) 500 { 501 return cp.real; 502 } 503 504 //四,算術運算符重載 : +,-,*,/,%等 505 //c#的運算符重載全部為靜態函數,因此沒有隱含參數 506 //而C++運算符重載時可以重載為友元,絕大多數重載為類的成員函數,因此基本都有一個隱含參數(對象本身) 507 public static CComplex operator +(CComplex a, CComplex b) 508 { 509 return new CComplex(a.real + b.real, a.image + b.image); 510 } 511 public static CComplex operator ++(CComplex cp) 512 { 513 cp.real++; 514 cp.image++; 515 return cp; 516 } 517 518 //五,不支持的運算符重載 519 //1,不允許重載=運算符, C++可以,都不允許重載+=之類的 520 //2,不允許重載括號()運算符 521 //3,不允許重載[]運算符,因為它是索引器 522 //public static implicit operator () (CComplex cp) 523 //{ 524 // return a; 525 //} 526 527 void TestPrivate() 528 { 529 var cp = new CComplex(1, 3); 530 cp.real = 20; 531 cp.image = 30.0f; 532 } 533 public void PrintInfo() 534 { 535 WriteLine("real:{0},image:{1}", real, image); 536 } 537 } 538 static void TestOperatorOverload() 539 { 540 CComplex cp = new CComplex(1, 1); 541 542 //1,同時支持前后向++,【不同于C++】 543 cp++; 544 ++cp; 545 546 //2,但不允許連++, 【不同于C++】 547 //cp++++或 ++++cp 548 549 cp.PrintInfo(); 550 551 //3,支持連續+,【同于C++】 552 CComplex cp1 = new CComplex(1, 1); 553 var cpadd = cp + cp1 + cp1 + cp1; 554 cpadd.PrintInfo(); 555 //類型轉換運算符 556 cp = 2.1f; 557 558 //類型轉換運算符 559 //C++中是調用類型轉換構造函數,而不是運算符重載 560 CComplex cp2 = 1.0f; 561 562 } 563 #endregion 564 #endregion 565 566 #region 2018.7.11 567 #region 兩數相加函數模板實現 568 static T MaxNum<T>(T a, T b) 569 { 570 return ((dynamic)a > (dynamic)b) ? a : b; 571 } 572 #endregion 573 #region thread lock 574 //thread test 575 class Account 576 { 577 private object thisLock = new object(); 578 int balance; 579 Random r = new Random(); 580 581 public Account(int initial) 582 { 583 balance = initial; 584 } 585 586 int Withdraw(int amount) 587 { 588 if (balance < 0) 589 { 590 throw new Exception("Negative Balance"); 591 } 592 593 lock (thisLock) 594 { 595 if (balance > amount) 596 { 597 WriteLine("before-withdraw: " + balance); 598 WriteLine("amount to withdraw: " + amount); 599 balance -= amount; 600 WriteLine("after withdraw: " + balance); 601 return amount; 602 } 603 else 604 return 0; //transaction rejected 605 } 606 } 607 608 public void DoTransactions() 609 { 610 for (int i = 0; i < 100; ++i) 611 { 612 Withdraw(r.Next(1, 100)); 613 } 614 } 615 616 } 617 618 static void TestObjectLock() 619 { 620 Account acc = new Account(1000); 621 Thread[] threads = new Thread[10]; 622 for (int i = 0; i < 10; ++i) 623 { 624 threads[i] = new Thread(acc.DoTransactions); 625 } 626 for (int i = 0; i < 10; ++i) 627 { 628 threads[i].Start(); 629 //threads[i].Join(); 630 } 631 632 633 } 634 #endregion 635 #region derive protected 636 class A 637 { 638 float fxPrivate; 639 protected int nProtected; 640 protected A(int x) { } 641 } 642 643 class B : A //c++的公有繼承 644 { 645 B(String name, int x) : base(x) { } 646 647 protected int nProtected; 648 void TestDerive() 649 {//這里的規則與C++完全一樣: 650 //1,子類不能訪問基類的私有成員,可以訪問基類的保護和公有成員 651 //2,保護成員可以在本類中訪問(不一定是本對象中) 652 nProtected = 20; 653 base.nProtected = 10; 654 var ob = new B("b", 1); 655 ob.nProtected = 30; //類中訪問類的保護成員,但不是本對象的成員 656 657 } 658 } 659 #endregion 660 #endregion 661 662 #region 2018.7.12 663 #region 常量和靜態變量靜態類readonly 664 //---------------------------------------------------------------------- 665 //常量和靜態變量,靜態類 666 //---------------------------------------------------------------------- 667 //類的靜態變量和常量,都屬于類而不屬于對象,不能用對象來調用,只能用類名調用 668 //這不同于C++,是更合理的設計 669 //常量的值在類定義時就確定了,不因對象而不同,因此存放在類中更合理 670 class CNormclass 671 { 672 class CInclass 673 { 674 public float fx = 20; 675 } 676 public int _id; 677 public const string cname = "CNormalclass"; 678 679 //1,常量僅能修飾 :數字,bool,字符串,null引用 680 //不能像C++那樣定義一個常量對象,這真是太悲哀了,因為很多時候這可以加速數據傳遞,增加安全性 681 //由于這個原因,C#的List.ToArray每次都只能返回一個內部數組的拷貝,因此使用list存儲數量較大較復雜的數據時 682 //不要輕易使用ToArray,直接用List就行了,它也支持下標索引方式取數組元素 683 const CInclass lst = null; 684 685 //2,readonly也不能實現常量對象的效果 686 //readonly僅表示變量本身不能被賦值,但不阻止通過對象變量更改對象內的字段 687 //onc.readonlyobj.fx = 20 688 public float fx = 20; 689 690 private readonly CInclass readonlyobj = new CInclass(); 691 692 public static void Test() 693 { 694 //1,不能調用非靜態字段或方法 695 //this._id = 20; //error,沒有this指針 696 697 //2,可以調用常量字段 698 var lname = cname; 699 700 var onc = new CNormclass(); 701 702 //私有變量在類的靜態方法也可以訪問 703 //2,雖然不能更改readonlyobj本身的值,卻可以更改其內部成員的值,這就是readonly的作用 704 onc.readonlyobj.fx = 20; 705 } 706 } 707 static class C712//類中類,默認為私有 708 {//靜態類不能實例化,且只能聲明:常量,靜態常量,靜態屬性,靜態方法 709 public const int constX = 20; //1,常量 710 public static int staticX = 0; //2,靜態常量 711 public static int ix { set; get; } //3,靜態屬性 712 713 //一,【靜態類中不能定義實例化字段】 714 //public int _id; 715 716 //二,【靜態類中不能定義實例化字段】 717 //void Ctest(){ //【error: 靜態類中不能定義實例化方法】 718 // this._id = 20; 719 //} 720 721 static void Test()//4,靜態方法 722 { 723 //三,【靜態方法中不能調用非靜態變量或方法,因為沒有this指針】 724 //_id = 20; //error 725 726 //四,【可以調用常量字段,這與C++不同】 727 var c = constX; 728 } 729 730 } 731 public const int ixd = 20; 732 public static float fx = 20; 733 public void Testff() 734 { 735 fx = 30; //等價于Program.fx = 30,而不是 this.fx = 30; 736 Program.fx = 30; 737 var tx = C712.constX; 738 C712.staticX = 30; 739 var ix = Program.ixd; 740 741 //var oc7 = new C712(); //error 靜態類不能創建實例 742 } 743 #endregion 744 #region 事件和委托 745 //-------------------------------------------------------------- 746 //event -test 747 //-------------------------------------------------------------- 748 //使用event的好處,與delegate的區別: 749 //event 本質上是一個委托,是做了一些安全措施的委托 750 //1,event 定義的委托只允許 +=操作,不允許=賦值,這樣防止事件被誤清空,delegate則沒有這些限制 751 //2,event 定義的委托只能在本類中調用,可以防止外部觸發,delegate沒有這些限制 752 //3,不使用事件,delegate方式完全可以實現類似限制,通過私有變量和公有函數結合方式 753 class EventTest 754 { 755 public delegate void Delx(string s = ""); 756 Delx _delegate; // 私有委托,防止外部調用 757 public event Delx _event; //公有事件,給外部通過+=注冊使用,但_event()函數只能在本類調用,不能在類外調用 758 759 //------------------------------------------------------------- 760 //1 ,委托方式 761 //------------------------------------------------------------- 762 //(1)外部調用eventTest.AddListener(func)方式注冊事件 763 public void AddListener(Delx callback) 764 { 765 _delegate += callback; 766 } 767 //(2)本類對象調用此函數觸發事件 768 void DelegateBrocast() 769 { 770 _delegate("delegate"); //回調,觸發事件 771 } 772 773 //------------------------------------------------------------- 774 //2,事件方式 775 //------------------------------------------------------------- 776 //(1)外部使用 _event += 方式注冊回調函數 777 //(2)本類對象調用此函數觸發事件 778 void EventBrocast() 779 { 780 _event("event");//回調,觸發事件 781 } 782 } 783 class Listener 784 { 785 public void OnEvent(string s) 786 { 787 WriteLine("on-event---------------" + s); 788 } 789 } 790 static void TestEventAndDelegate() 791 { 792 Listener l1 = new Listener(); 793 EventTest test = new EventTest(); 794 795 //1,事件方式 796 test._event += l1.OnEvent; //注冊事件 797 //test._event = l1.OnEvent; //編譯錯誤,事件只能使用+=,防止事件被清空 798 //test._event("event"); //編譯錯誤,事件不能在類外調用,事件只能由其所在類調用 799 800 //2,委托方式 801 test.AddListener(l1.OnEvent); //注冊委托,通過函數對委托進行注冊,因委托是私有的,可防止直接操作 test._delegate() 802 } 803 804 #endregion 805 #region 文件和目錄 806 static void FileAndDirectory() 807 { 808 //------------------------------------------------------------------------- 809 //文件對象的相關操作 810 //------------------------------------------------------------------------- 811 //方式一,使用工具類:File類,不需生成對象 812 var file = File.Open("f:/test.txt", FileMode.Create, FileAccess.ReadWrite); 813 //方式二,通過FileStream的對象 814 var filestream = new FileStream("f:/test._txt", FileMode.Create, FileAccess.ReadWrite); 815 816 //------------------------------------------------------------------------- 817 //目錄文件相關操作 818 //------------------------------------------------------------------------- 819 //方式一,實例化DirectoryInfo類 820 var dir = new DirectoryInfo("f:/tolua"); 821 //(1)獲取目錄 822 foreach (var d in dir.GetDirectories("*.*", SearchOption.AllDirectories)) 823 { 824 WriteLine(d.FullName); 825 } 826 //(2)獲取文件 827 foreach (var fileinfo in dir.GetFiles("*.*", SearchOption.AllDirectories)) 828 { 829 WriteLine(fileinfo.FullName); 830 } 831 832 //方式二,使用工具類: Directory類,不需生成對象 833 //(1)獲取目錄 834 var dirs = Directory.GetDirectories("f:/tolua", "*.*", SearchOption.AllDirectories); 835 //(2)獲取文件 836 dirs = Directory.GetFiles("f:/tolua", "*.*", SearchOption.AllDirectories); 837 838 for (int i = 0; i < dirs.Length; ++i) 839 {//打印輸出 840 WriteLine(dirs[i]); 841 } 842 843 } 844 #endregion 845 #endregion 846 847 #region 2018.7.17 848 #region 計算機中浮點數的存儲 849 static void TestFloat() 850 { 851 using (var ms = new MemoryStream()) 852 { 853 854 using (var br = new BinaryWriter(ms)) 855 { 856 br.Write(125.5f); 857 var bytes = ms.GetBuffer(); 858 } 859 } 860 unsafe 861 { 862 float fx = 125.5f; 863 int* pfx = (int*)(&fx); 864 } 865 866 } 867 868 #endregion 869 #region 位移運算 870 static void TestBitShift() 871 { //---------------------------------------------------------------------------- 872 //十進制數轉二進制: 873 //1,原理:將數X右移1位,最低位被移出,再左移,得到了數X0,則x-x0即為最低位的值 874 //2,手工算法:根據1的原理,不斷的對一個數整除2得余數,了終得到余數序列即是二進制的反向序列 875 //3,左移等價于乘2,右移等價于除2,原理是乘法的豎式算法, 876 // 101 877 //x 010 878 //------- 豎式算法適用于任何進制的加減法和乘法運算 879 // 000 880 //+101 881 //------- 882 // 1010 883 //---------------------------------------------------------------------------- 884 885 int x = 7; 886 List<Byte> bits = new List<Byte>(4); 887 while (x != 0) 888 { 889 var left = x - ((x >> 1) << 1);//<=> x - x/2*2 890 bits.Add((byte)left); 891 x = x >> 1; 892 } 893 } 894 #endregion 895 #region IEnumerableAndLinQ 896 class Product 897 { 898 public int cateId; 899 public string name; 900 } 901 class Category 902 { 903 public int id; 904 public string name; 905 } 906 public static void TestIEnumerableAndLinq() 907 { 908 Category[] cates = new Category[] 909 { 910 new Category{id = 1, name = "水果"}, 911 new Category{id = 2, name = "飲料"}, 912 new Category{id = 3, name = "糕點"}, 913 }; 914 915 Product[] products = new Product[] 916 { 917 new Product{cateId=1, name = "apple"}, 918 new Product{cateId=1, name = "banana"}, 919 new Product{cateId=1, name = "pear/梨"}, 920 new Product{cateId=1, name = "grape/葡萄"}, 921 new Product{cateId=1, name = "pineapple/菠蘿"}, 922 new Product{cateId=1, name = "watermelon/西瓜"}, 923 new Product{cateId=1, name = "lemon/檸檬"}, 924 new Product{cateId=1, name = "mango/芒果"}, 925 new Product{cateId=1, name = "strawberry/草莓"}, 926 new Product{cateId=2, name = "bear/啤酒"}, 927 new Product{cateId=2, name = "wine"}, 928 new Product{cateId=3, name = "cake"}, 929 new Product{cateId=3, name = "basicuit/餅干"}, 930 931 }; 932 var rets = cates.Where((x) => { return x.id > 1 && x.id < 5; }); 933 var iter = rets.GetEnumerator(); 934 935 while (iter.MoveNext()) 936 { 937 //WriteLine(iter.Current); 938 } 939 940 var set = from c in cates 941 942 //這里只能寫一個條件,就是equals,用來關聯兩個表 943 //并且 c相關的條件只能寫在equals左邊,p相關條件只能寫equals右邊 944 join p in products on c.id equals p.cateId 945 946 //這里存放的是 products中的元素合集,而不是cates中的元素合集 947 //如果 from p in products join c in cates on c.id equals p.id into xgroups 948 //則xgroups中放的是cates中的元素集合 949 950 //這里是說將products中cateId等于c.id的所有元素放入一個組xgroups中 951 into xgroups 952 orderby c.id descending //對set中的結果進行降序排列 953 954 //where m > 4 && m < 10 //這里就可以寫多個條件了 955 956 //from in 相當于外層循環,join in 相當于內層循環 957 //select在雙層循環體中,每執行一次循環,【如果符合條件】,則執行一次結果選擇 958 //雙層循環完成后,最終將很多條選擇提交給set 959 //【注意,如果不符合條件 select不會執行】 960 select new { cate = c.name, grp = xgroups }; //可以生成一個新的對象 961 962 foreach (var p in set) 963 { 964 WriteLine("分組:" + p.cate); 965 foreach (var g in p.grp) 966 { 967 WriteLine(g.cateId + "," + g.name); 968 } 969 } 970 } 971 972 #endregion 973 #region 類和繼承 974 class CTestX 975 { 976 public virtual void OnUpdate() 977 { 978 Console.WriteLine("base-on-update"); 979 } 980 public virtual void OnUpdate2() 981 { 982 Console.WriteLine("base-on-update2"); 983 } 984 public void Update() 985 { 986 this.OnUpdate(); //注釋1,如果子類有overide則調用子類的,否則調用自己的 987 } 988 989 public CTestX() 990 { 991 992 } 993 protected CTestX(float fx) 994 { 995 WriteLine("CTestX"); 996 } 997 998 ~CTestX() 999 { 1000 WriteLine("~Ctestx"); 1001 } 1002 public float fx; 1003 string name; 1004 } 1005 1006 //子類不能訪問基類任何私有的東西,包括方法,字段,屬性,但它們都被繼承了,屬于子類,從實例內存可證 1007 //方法包括構造函數,即當基類是私有構造函數時,子類無法在初始化列表中調用base()來初始化 1008 class CTestChildX : CTestX 1009 { 1010 CTestX otestx; 1011 1012 public CTestChildX() : base(1)//當基類為私有構造時,這里base無法調用 1013 {//當基類沒有無參構造函數時,必須在初始化列表中初始化所有成員對象,如otestx 1014 WriteLine("CTestChildX"); 1015 } 1016 1017 //注意overide與virtual的區別: 1018 //1,overide : 表明【函數是對基類的重寫】 且 【本身是虛函數可被子類重寫】 1019 //【函數會與基類、子類發生虛函數機制】 1020 //2,virtual : 僅表明函數是個虛函數,不會與基類發生虛函數機制 1021 //如果子類overide了該函數,則會與子類發生虛函數機制 1022 //3,多級繼承中只要有一級沒override,虛函數機制就會打斷在此層級,見 1023 1024 //override在編譯層的機制是重寫虛函數表中的函數地址 1025 //即將繼承而來的虛函數表中的虛函數地址替換成本類的虛函數地址 1026 public static void TestDerive() 1027 { 1028 // CTestX ox = new CTestChildX(); 1029 // ox.OnUpdate(); //base-on-update,無虛函數機制發生 1030 // ox.OnUpdate2(); //child-on-update2,虛函數機制發生 1031 // ox = new CTestY(); 1032 // ox.OnUpdate(); //base-on-update,無虛函數機制發生 1033 CTestChildX ocx = new CTestZ(); 1034 ocx.OnUpdate(); //grand-child-on-update 1035 } 1036 1037 public override void OnUpdate() 1038 { 1039 Console.WriteLine("child-on-update"); 1040 } 1041 public override void OnUpdate2() 1042 { 1043 Console.WriteLine("child-on-update2"); 1044 } 1045 1046 ~CTestChildX() //不支持virtual 1047 { 1048 WriteLine("~CTestChildX"); 1049 } 1050 } 1051 1052 class CTestY : CTestChildX 1053 { 1054 public override void OnUpdate() 1055 { 1056 Console.WriteLine("grand-child-on-update"); 1057 1058 } 1059 } 1060 class CTestZ : CTestY 1061 { 1062 //因為這里的Update不是虛函數,因此 1063 public void OnUpdate() 1064 { 1065 Console.WriteLine("grand-grand-child-on-update"); 1066 1067 } 1068 } 1069 1070 1071 struct CTX 1072 { 1073 void Test() {//不支持C++的const語法 1074 } 1075 } 1076 1077 //1,不能繼承結構,可以實現接口, 1078 //2,不能有虛函數 1079 struct CCTX //: CTX 1080 { 1081 public void Test() 1082 { 1083 1084 } 1085 } 1086 1087 #endregion 1088 #region 字符串格式化 1089 static void TestStrFormat() 1090 { 1091 var str = Console.ReadLine(); 1092 while (str != "exit") 1093 { 1094 int ix; 1095 Int32.TryParse(str, out ix); //ix = 120 1096 var f1 = string.Format("{0 :d5}", ix); //"00120" 1097 var f2 = string.Format("{0,-10:d5}", ix);//"00120 " 1098 var f3 = string.Format("{0:x}", ix); //16進制輸出到字符串 1099 var f4 = string.Format("{0:0.000}", ix);//浮點數 120.000 1100 Console.WriteLine("-----------begin-------------"); 1101 Console.WriteLine(f1); 1102 Console.WriteLine(f2); 1103 Console.WriteLine(f3); 1104 Console.WriteLine(f4); 1105 Console.WriteLine("------------end-------------"); 1106 1107 str = Console.ReadLine(); 1108 } 1109 } 1110 #endregion 1111 #endregion 1112 1113 #region 2018.7.25 1114 #region 引用返回值(不是右值引用) 1115 static int[] _bookNum = new int[] { 1, 2, 3, 4, 5, 6 }; 1116 static ref int GetBookNumber(int i) 1117 { 1118 int x = 10; 1119 return ref _bookNum[i]; 1120 } 1121 static void TestRefReturn() 1122 { 1123 ref int rfn = ref GetBookNumber(1); 1124 rfn = 10101; //_bookNum[1]變成了 10101 1125 int vn = GetBookNumber(2); 1126 vn = 202; //_bookNum[2]未變,仍為3 1127 1128 ref int x = ref vn; 1129 } 1130 #endregion 1131 #region 索引器 1132 class mylist<T> 1133 { 1134 const int defaultCap = 4; 1135 T[] items; 1136 int count; 1137 int cap = defaultCap; 1138 public mylist(int cap = defaultCap) 1139 { 1140 if (cap != defaultCap) 1141 this.cap = cap; 1142 items = new T[cap]; 1143 } 1144 public T this[int idx] { 1145 set { 1146 items[idx] = value; 1147 } 1148 get { 1149 return items[idx]; 1150 } 1151 } 1152 1153 } 1154 enum Color 1155 { 1156 red, 1157 green, 1158 blue, 1159 yellow, 1160 cyan, 1161 purple, 1162 black, 1163 white, 1164 } 1165 1166 static void TestIndexer(Color clr = Color.black) 1167 { 1168 mylist<string> lst = new mylist<string>(); 1169 lst[1] = "hello"; 1170 } 1171 #endregion 1172 #region 部分類 1173 //部分類的作用是可以把一個龐大的類拆分到多個文件,每個文件實現一部分 1174 //而不是實現像C++那樣將聲明與實現分開 1175 //若要實現聲明(接口)與實現分開,應該使用抽象類或接口 1176 partial class CPartclass 1177 { 1178 public void ShowName() { 1179 WriteLine("show name"); 1180 } 1181 } 1182 1183 partial class CPartclass 1184 { 1185 public void ShowAge(){ 1186 WriteLine("show age"); 1187 } 1188 } 1189 static void TestPartclass() 1190 { 1191 CPartclass opc = new CPartclass(); 1192 opc.ShowName(); 1193 opc.ShowAge(); 1194 } 1195 #endregion 1196 #region 動態分配對象數組C#與C++的區別 1197 struct xobject 1198 { 1199 public float fx, fy, fz; //全是public的 1200 } 1201 static void TestDynamicAllocInCSharpCpp() 1202 { 1203 //1,對于引用類型數組,需要兩步才能完成,因為數組中存放的是對象的引用 1204 //1.1 c#中 1205 xobject[] arr = new xobject[2];//這時候,只是分配了一個引用數組,arr[0],arr[1]均為null 1206 for (int i = 0; i < 2 ; i++) 1207 { 1208 arr[i] = new xobject(); //為數組中每個引用申請對象 1209 } 1210 1211 //1.2 c++中 1212 //xobject** pp = new xobject*[2]; 1213 //pp[0] = new xobject(); 1214 //pp[1] = new xobject(); 1215 1216 //2 對于值類型數組,則只需一步,因為數組中放的就是值,這在C#與CPP中都一樣 1217 //2.1 C#中 1218 int[] iarr = new int[2]; 1219 var a0 = iarr[0]; //0 1220 var a1 = iarr[1]; //0 1221 1222 xobject[] varr = new xobject[3]; 1223 varr[0].fx = 0.1f; 1224 varr[1].fy = 2.5f; 1225 varr[2].fz = 12; 1226 1227 //2.2,在C++中 1228 //xobject* pobjs = new xobject[2]; //每個數組元素都是一個值類型對象 1229 //pobjs[0].fx = 20; 1230 } 1231 #endregion 1232 #region Object?語法 1233 static void TestobjAsk() 1234 { 1235 object obj = "hello"; 1236 WriteLine(obj?.ToString());//如果obj不為null則調用tostring 1237 } 1238 #endregion 1239 #region C#默認字符編碼及系統默認編碼 1240 //默認編碼為unicode,字符串本身的編碼并不重要,字節讀寫時指定的編碼才重要,如下面的BinaryWriter 1241 //Encoding.Default是當前系統的默認編碼,并不是c#字符串的默認編碼 1242 //Encoding.Default規則:漢字2字節,其它1字節 1243 static void TestDefaultStrEncoding() 1244 { 1245 string str = "hdd好"; 1246 1247 using (var ms = new MemoryStream()) 1248 { 1249 using (var br = new BinaryWriter(ms, Encoding.Default)) 1250 { 1251 br.Write(str); 1252 var len = ms.Length-1; 1253 WriteLine(len); 1254 1255 } 1256 } 1257 } 1258 #endregion 1259 #region 屬性attribute和反射 1260 class ReflectableClass 1261 { 1262 public float fx; 1263 public string str; 1264 //static const int x = 20; //這在C++中是可以的 1265 public void Printstr(string str, int idx) 1266 { 1267 WriteLine(str + ":" + idx); 1268 } 1269 } 1270 static void TestReflect() 1271 { 1272 1273 ReflectableClass ox = new ReflectableClass(); 1274 Type t = typeof(ReflectableClass);//Type.GetType("ConsoleApplication1.Program.ReflectableClass");//ox.GetType(); 1275 var tname = t.GetField("name"); 1276 var tfx = t.GetField("fx"); 1277 var func = t.GetMethod("Printstr", new Type[] {typeof(string),typeof(int) }); 1278 func.Invoke(ox, new object[] { "helloworld", 1 }); 1279 1280 1281 Type Ts = Type.GetType("System.String"); 1282 var fs = Ts.GetMethod("Substring", new Type[] { typeof(int), typeof(int) }); 1283 var subs = fs.Invoke("hello world", new object[] { 1, 5 }); 1284 WriteLine(subs); 1285 } 1286 1287 static void TestAttribute() 1288 { 1289 1290 } 1291 1292 #endregion 1293 #endregion 1294 #region JSON 1295 void TestJson() 1296 { 1297 1298 } 1299 1300 #endregion 1301 #region CPP與CS間數據傳遞轉換 1302 1303 #endregion 1304 #region 線程 1305 #endregion 1306 #region 線程池 1307 #endregion 1308 #region 任務 1309 #endregion 1310 #region 程序集 1311 #endregion 1312 #region 多線程調試 1313 #endregion 1314 #region 擴展方法測試 1315 static void TestExtMethod() 1316 { 1317 ExtTargetCls oet = new ExtTargetCls(); 1318 oet.methodExt(100); 1319 WriteLine(oet.sum); 1320 } 1321 #endregion 1322 class CMyList 1323 { 1324 //readonly僅表示變量本身不能被賦值,但不阻止通過對象變量更改對象內的字段 1325 //on._id = 100 //ok 1326 //on = new CNormclass() //error 1327 1328 public readonly int[] rarr = { 1, 2, 3, 4 }; 1329 public readonly int rix = 30; //可在初始化時賦值 1330 public readonly CNormclass on = new CNormclass(); 1331 public CMyList() 1332 { 1333 rix = 1; //可在構造函數中賦值 1334 } 1335 public int[] toArray() 1336 { 1337 return rarr; 1338 } 1339 1340 public void Clear() 1341 { 1342 for(var i=0; i < rarr.Length; ++i) 1343 { 1344 rarr[i] = 0; 1345 } 1346 } 1347 public static implicit operator CMyList(int ix) 1348 { 1349 return new CMyList(); 1350 } 1351 1352 } 1353 1354 // ctrl + w, t 可以察看所有待做任務 1355 static void Main(string[] args) 1356 { 1357 TestExtMethod(); 1358 //TestReflect(); 1359 //TestDefaultStrEncoding(); 1360 //TestDynamicAllocInCSharpCpp(); 1361 //TestPartclass(); 1362 //TestRefReturn(); 1363 //TestOperatorOverload(); 1364 // CTestChildX.TestDerive(); 1365 //TestFloat(); 1366 1367 //var arr = returnArray(); 1368 1369 } 1370 1371 } 1372 #region 擴展方法 1373 sealed class ExtTargetCls 1374 { 1375 public float sum = 10; 1376 } 1377 //擴展方法必須在頂級靜態類中定義,不能是內部類 1378 //能不能通過擴展方法來修改類庫以達到不法目的? 不能,因為擴展方法只能修改類的公有成員 1379 static class ExtentMethod 1380 { 1381 public static void methodExt(this ExtTargetCls target, float add) 1382 { 1383 target.sum += add; 1384 } 1385 } 1386 #endregion 1387 }?
posted on 2018-07-30 16:56 時空觀察者9號 閱讀(...) 評論(...) 編輯 收藏
總結
- 上一篇: #region 常量和静态变量静态类re
- 下一篇: C#抽象类与接口的区别【转】