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 (Comparisoncomparison) 216 //public delegate int Comparison(T x, T y); 217 218 219 //这是对调用List.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//类入口 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 ()//注意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 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 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 //多个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 : CTClass 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 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 bits = new List (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 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 }