2018.8.14-C#复习笔记总
using System; using System.Collections.Generic; //using System.Linq; using System.Text; using System.Diagnostics; using System.IO; using static System.Console; using System.Linq; using System.Runtime.InteropServices; using System.Threading; using System.Text.RegularExpressions; using System.Threading.Tasks; using System.Security.Permissions; using System.Net.Sockets; using System.Net; namespace ConsoleApplication1 { class Program { [DllImport("dltest.dll", EntryPoint ="Print")] static extern void xPrint(int x); #region old-test ////////////////////////////////////////////////////////////////// static void TestStreamReadWrite() {//一个流不能兼备读写两种操作,不知道为什么,这不合理 string s1 = "你好啊ABC"; var t = "你好啊ABC".Length; //关于编码注意几点: //1,sizeof(char) 等于 2 //2,str.Length 是以元素个数算的,不是按字节算的,如 "你好啊ABC” length = 6 //3,c#在VS的默认编码为 Encoding.Default, 该编码下汉字占两字节,非汉字占1字节,通过查看ms中的字节数据可知 //4,string 类型写入任何buffer时都是先写长度,一般为1字节,再写字节数据,如下 var sz = sizeof(char); //2, 注意char占2字节 var szb = sizeof(bool); //1 var ms = new MemoryStream(); var writer = new BinaryWriter(ms, Encoding.UTF7); writer.Write(s1); ms.Close(); writer.Close(); var ms2 = new MemoryStream(ms.GetBuffer()); var reader = new BinaryReader(ms2, Encoding.UTF8); var s2 = reader.ReadString(); } ////////////////////////////////////////////////////////////////// static void TestEncoding() { string s1 = "你好啊ABC"; //汉字乱码问题,汉字必须使用2个以上字节才能表示 //编码方式 //1,ASCII码,只有一个字节,不能正确表示汉字,出现乱码,可以正确表示数字和字母符号 //2,UNICODE,任何符号都用2个字节表示,因此可以表示汉字和任意符号 //3,UTF8,变字节的编码,可以正确表示任何字符和汉字,各国语言 //4,GB2312编码,国标码,主要是为汉字服务的中国编码,汉字占两字节,字母数字占1字节 //5,default编码,在国内, 般就是GB2312 Encoding.Default.GetBytes(s1); var bytes = Encoding.GetEncoding("GB2312").GetBytes(s1); var len = bytes.Length; var bts = new byte[10 + len]; Array.ConstrainedCopy(bytes, 0, bts, 0, len); var s2 = Encoding.GetEncoding("GB2312").GetString(bts).TrimEnd('\0'); string s3 = "\0hello/0/0dddddd".TrimStart('\0');//!!!!!!!!!!!!!!!!!!!!!!!!!!!! } #region 计算机中数据的存储 ////////////////////////////////////////////////////////////////// static void TestTypeConvert() {//把一个有符号数转为无符号后再转回来值保持不变,以下以1字节为例 //原理:计算机中符点数都是有符号的,不存在这种转变,只剩下整数, //真值:绝对值的二进制值,如-1的真值为 00000001 //整数是以补码形式存放的,计算机规定了正数的补码是本身,负数的补码是:符号位不变,真值按位取反再加1 //强制转换做的事就是把一个补码看成是有符号还是无符号 //有符号数,在计算时:符号位不变,真值按位取反再加1。无符号数直接计算,举例如下: //1,-1 的真值为00000001,补码为 1 111 1111,强转时就是把补码值看作是一个无符数,因此它=255 //,再次强转时把它看成有符号数,符号位不管,其余位按位取反加1后是1,因此再次转回了-1 //2,-2 的真值为00000010,补码为 1 111 1110,强转时把补码看作无符号数,因此它=254 //3,-128真值有点特殊,128的二进制码为1000 0000,第8位是符号位,舍弃,取后面的0,即-128的真值为0 //补码经按位取反加1后还是 1 000 0000,强转时看成无符号数即为128 //------------------------------------------- //1字节数据和2字节数据进行加法运算时,要进行位扩展,将1字节扩展为2字节 //正数扩展时高位补0,负数扩展时高位补1 //C#中小于4字节的数据进行运算时会先扩展成int再进行 sbyte sb = -127; var b = (byte)(sb); var sb1 = (sbyte)(b); object dx = 10.0f; double dx2 = 33; byte ix = (byte)dx2; var t = dx.GetType(); Type T = System.Type.GetType(t.FullName, true); } #endregion ////////////////////////////////////////////////////////////////// void TestUncheck() { unchecked {//不被编译系统做编译时安全检查 } } static void TestBoxing() { int i = 10; object o = 1; int i2 = (int)o; } static void TestReadBytes() { byte[] bts = new byte[4] { 23, 0, 16, 0 }; var ms = new MemoryStream(bts); var br = new BinaryReader(ms); var p1 = ms.Position; var ix = br.ReadUInt32(); var p2 = ms.Position; Console.WriteLine("num=" + ix); br.Dispose(); br.Close(); ms.Dispose(); ms.Close(); } static void TestStrEnd() { string str = "abcde\0"; var br = new BinaryReader(new MemoryStream(Encoding.ASCII.GetBytes(str))); var b = br.ReadByte(); while (b != 0) { Console.WriteLine(b); try { b = br.ReadByte(); } catch (System.Exception ex) { Console.WriteLine("未发现字符串结束符"); break; } } } static void TestBigEndia() { var br = new BinaryWriter(File.Create("f:/testx.dt"), Encoding.ASCII); br.Write((Int16)9); string str = "Stand"; br.Write(str); br.Write((Int16)10); br.Write((Int16)70); br.Dispose(); } static void TestChar0() {//注意字符串中0和\0的区别,如 s1="h0ello", s2 = "h\0ello" //s2中的\0是字符串结尾符,除了C#不把它作为结束符外,其它语言都把它作为结束符,如U3D,LUA,C/C++等 //而s1中的0仅是一个字符0而已,字符0的ASCII值是0X31=49,'\0'的ASCII值是0 //注意这两种0在C#和U3D的API之间切换时容易造成BUG,如: //1, debug.log(s1): "h0ello" //2,debug.log(s2): "h" var s = "hello"; s += 0 + ",world"; var s1 = "hello"; s1 += (char)0 + ",world"; var s2 = "hello"; s2 += '\0' + ",world"; } static void MemTest() { } static void ReflectionTest() {//测试两种反射的效率问题 //Type.GetType()只能在同一个程序集中使用,typeof则可以跨程序集(assembly) //通过下面的实测,发现typeof是比GetType快40多倍 var timer = Stopwatch.StartNew(); timer.Start(); Type tx = Type.GetType("string"); var tx1 = Type.GetType("float"); timer.Stop(); Console.WriteLine("T1= " + timer.Elapsed);//0.0000471 timer.Restart(); tx = typeof(string); tx1 = typeof(float); timer.Stop(); Console.WriteLine("T2= " + timer.Elapsed);//0.0000011 } static void TestDelegate() { //类C++11风格:指定初始化容量20,使用初始化列表给部分成员赋值 var lst = new List<float>(20) { 1, 3, 4, 20, -2, 9, 0 }; for (var i = 0; i < lst.Count; ++i) { //使用下标进行随机访问,说明list不是一个真正的链表,而是类似STL的Vector Console.WriteLine(lst[i]); } //public void Sort (Comparison<T> comparison) //public delegate int Comparison<T>(T x, T y); //这是对调用List<int>.Sort进行排序的写法,其中sort的定义及Comparison委托的定义如上 lst.Sort(new Comparison<float>(delegate (float m1, float m2) //委托 { return 1; })); lst.Sort(delegate (float m1, float m2) //委托 { return 1; }); lst.Sort((float m1, float m2) =>//Linq表达式 { return 1; }); lst.Sort((m1, m2) => //Linq表达式 { return 1; }); } static string TestRetStr() {//测试返回字符串是否会复制 return "helloworld"; } static void TestStrRet() {//h1 = h2 = h3说明它们返回的是同一个字符串的引用 var s1 = TestRetStr(); var s2 = TestRetStr(); var s3 = TestRetStr(); var h1 = s1.GetHashCode(); var h2 = s1.GetHashCode(); var h3 = s1.GetHashCode(); } static void TestVirtualFuncCall() { var otx = new CTestChildX(); otx.Update();//输出结果:child,如果注释1处函数不加override,输出结果为:base var oty = new CTestY(); oty.Update(); oty.OnUpdate(); } static void TestStrModify() { var s1 = "hello"; var s2 = s1; s1 += "world"; Console.WriteLine(s2); var uns1 = s2.GetHashCode(); Console.WriteLine(uns1); } static void Tests1() { var s1 = "hello"; var uns1 = s1.GetHashCode(); Console.WriteLine(uns1); } #endregion #region 2018.3.30 #region ref out and template class myTemp<T1, T2>//类入口 { public T1 Add(T1 a, T1 b) {//模板类型不能直接相加,必须先转为动态类型,避开编译检查,运行时动态决定类型 dynamic da = a; dynamic db = b; return da + db; } public void tint<T3>()//注意C++不能这么写,所有模板参数必须由类入口传入 { Type t = typeof(T3); WriteLine(t); } } delegate void refOutFunc(ref double t1, out double t2); delegate T TemplateDelegate<T, U>(T a, U b); static void TestRefAndOut() { //ref, out 本质上都是引用 //fef就为了传给函数使用,必须先初始化,但也可以传出数据,out是为了从函数中传出数据使用,不用初始化 refOutFunc rof = delegate (ref double ax, out double bx) { ax = 1; bx = 2;//ref out两种类型的变量都被更改了 }; double x1 = 0, x2; rof(ref x1, out x2); } static void TestTemplate() { var otp = new myTemp<int, int>(); otp.tint<object>(); } static T TempFunc<T, U>(T a, U b) { return a; } static void TestBufAligin() {//自定义字节BUF的对齐测试 int x = 9; int y = (x + 7) & ~7; WriteLine(y); } #endregion #endregion #region 2018.4.9 //BUG?????? //使用StopWatch测试运行时间 //两段测试A和B //测试结果受测试顺序影响,后测要比先测耗时长了许多 static void TestKeyIntStr() {// var idict = new Dictionary<int, string>(); var sdict = new Dictionary<string, string>(); for (int i = 0; i < 1000000; i++) { var key = i * 2 + 1; var v = i * i + ""; idict.Add(key, v); sdict.Add(key + "", v); } //测试 A var t1 = 100000 * Test1(idict); //测试 B var t2 = 100000 * Test2(sdict); Console.WriteLine("t1: {0},t2: {1}", t1, t2); //Console.WriteLine("dt1: {0},dt2: {1}", dt1, dt2); } static float Test1(Dictionary<int, string> dict) { var timer = new Stopwatch(); timer.Start(); var it = dict[2001]; var t1 = timer.ElapsedTicks; timer.Stop(); return (float)((float)t1 / Stopwatch.Frequency); } static double Test2(Dictionary<string, string> dict) { var timer = new Stopwatch(); timer.Start(); var it = dict["2001"]; var t1 = timer.ElapsedTicks; timer.Stop(); return (float)((float)t1 / Stopwatch.Frequency); } #endregion #region 2018.7.7 #region 数组的数组,二维数组 static int[] returnArray() { //数组是引用类型,分配在堆上 int[] arr = { 1, 2, 3, 4 }; //虽然这样写,其实等价于int[] arr = new int[]{1,2,3,4}; return arr; //返回一个数组对象 } static void TestArray() { //1,一维数组 char[] arr = new char[2] { 'a', 'b' }; //必须全部初始化,或不初始化 int[] iarr = new int[2] { 0, 1 }; char[] sarr = new char[3]; //2,数组的数组,锯齿数组 char[][] d2arr = new char[2][]; d2arr[0] = new char[30]; d2arr[1] = new char[2] { 'a', 'b' }; d2arr[0][1] = 'x'; //3,二维数组,矩阵 int[,] i2arr = new int[2, 3]; for (var i = 0; i < 2; ++i) { for (var j = 0; j < 3; ++j) { i2arr[i, j] = i * 3 + j; } } } #endregion #region 字段初始化无法使用非静态(字段、方法、属性) delegate int mydelegate(int x); //------------------------------------------------------------------------- //字段初始化无法使用非静态(字段、方法、属性) //------------------------------------------------------------------------- float fxs; static float sfxs; //float fxs2 = fxs; //error float fxs3 = sfxs; //right,可用静态字段初始化 float fxs4 = TestStaticInit(); //right,调用静态函数初始化 static int TestStaticInit() { return 10; } mydelegate _mydel = (x) =>//LINQ为什么可以?,从下面可知,LINQ语句只相当于一堆初始化语句的集合 { //int fx = fxs; //error return 20; }; #endregion #region 默认访问修饰符 //1,名字空间中,最外层类及接口的默认修饰符为internal,也就是本程序集可访问 //2,类中,变量,成员,类中类的默认修饰符为private //3,结构中,同类 //4,接口中,所有方法和属性都为public,接口中只能有方法,不能有变量 interface IMyinterface {//接口中可以有方法,抽象属性,不可以有变量 int Id { get; } //抽象属性,公有 void Fly(); //方法,公有 } #endregion #region 类模板继承 class CTClass<t1, t2, t3> //多个where的写法 where t1 : struct //必须是值类型 where t2 : class //必须是引用类型 where t3 : new() //必须有无参构造函数 { float fx, fy; public static t1 Add(t1 a, t1 b) { return (dynamic)a + (dynamic)b; } } //模板继承的几种方式 //1,全特化 class CDTClass : CTClass<int, CCmpBase, CCmpBase> { } //2,原样继承,注意基类的所有约束都要重写一遍 class CDTX<t1, t2, t3, t4> : CTClass<t1, t2, t3> where t1 : struct //必须是值类型 where t2 : class //必须是引用类型 where t3 : new() //必须有无参构造函数 { } //3,偏特化,介于二者之间的形态 #endregion #region 运算符重载 class CCmpBase {//带有默认构造函数 float _x; } class CComplex : CCmpBase { float real, image; public CComplex(float real, float image = 0) { this.real = real; this.image = image; } //一,类型转换 :数值转对象 //CComplex cp = 2.1f 或 CComplex cp; cp = 2.1f; //C#从不调用类型转换构造函数进行类型转换 public static implicit operator CComplex(float real) { return new CComplex(real); } //二,类型转换:对象转bool public static explicit operator bool(CComplex cp) { return cp.real != 0 && cp.image != 0; } //三,类型转换:对象转数值 public static implicit operator float(CComplex cp) { return cp.real; } //四,算术运算符重载 : +,-,*,/,%等 //c#的运算符重载全部为静态函数,因此没有隐含参数 //而C++运算符重载时可以重载为友元,绝大多数重载为类的成员函数,因此基本都有一个隐含参数(对象本身) public static CComplex operator +(CComplex a, CComplex b) { return new CComplex(a.real + b.real, a.image + b.image); } public static CComplex operator ++(CComplex cp) { cp.real++; cp.image++; return cp; } //五,不支持的运算符重载 //1,不允许重载=运算符, C++可以,都不允许重载+=之类的 //2,不允许重载括号()运算符 //3,不允许重载[]运算符,因为它是索引器 //public static implicit operator () (CComplex cp) //{ // return a; //} void TestPrivate() { var cp = new CComplex(1, 3); cp.real = 20; cp.image = 30.0f; } public void PrintInfo() { WriteLine("real:{0},image:{1}", real, image); } } static void TestOperatorOverload() { CComplex cp = new CComplex(1, 1); //1,同时支持前后向++,【不同于C++】 cp++; ++cp; //2,但不允许连++, 【不同于C++】 //cp++++或 ++++cp cp.PrintInfo(); //3,支持连续+,【同于C++】 CComplex cp1 = new CComplex(1, 1); var cpadd = cp + cp1 + cp1 + cp1; cpadd.PrintInfo(); //类型转换运算符 cp = 2.1f; //类型转换运算符 //C++中是调用类型转换构造函数,而不是运算符重载 CComplex cp2 = 1.0f; } #endregion #endregion #region 2018.7.11 #region 两数相加函数模板实现 static T MaxNum<T>(T a, T b) { return ((dynamic)a > (dynamic)b) ? a : b; } #endregion #region thread lock //thread test class Account { private object thisLock = new object(); int balance; Random r = new Random(); public Account(int initial) { balance = initial; } int Withdraw(int amount) { if (balance < 0) { throw new Exception("Negative Balance"); } lock (thisLock) { if (balance > amount) { WriteLine("before-withdraw: " + balance); WriteLine("amount to withdraw: " + amount); balance -= amount; WriteLine("after withdraw: " + balance); return amount; } else return 0; //transaction rejected } } public void DoTransactions() { for (int i = 0; i < 100; ++i) { Withdraw(r.Next(1, 100)); } } } static void TestObjectLock() { Account acc = new Account(1000); Thread[] threads = new Thread[10]; for (int i = 0; i < 10; ++i) { threads[i] = new Thread(acc.DoTransactions); } for (int i = 0; i < 10; ++i) { threads[i].Start(); //threads[i].Join(); } } #endregion #region derive protected class A { float fxPrivate; protected int nProtected; protected A(int x) { } } class B : A //c++的公有继承 { B(String name, int x) : base(x) { } protected int nProtected; void TestDerive() {//这里的规则与C++完全一样: //1,子类不能访问基类的私有成员,可以访问基类的保护和公有成员 //2,保护成员可以在本类中访问(不一定是本对象中) nProtected = 20; base.nProtected = 10; var ob = new B("b", 1); ob.nProtected = 30; //类中访问类的保护成员,但不是本对象的成员 } } #endregion #endregion #region 2018.7.12 #region 常量和静态变量静态类readonly //---------------------------------------------------------------------- //常量和静态变量,静态类 //---------------------------------------------------------------------- //类的静态变量和常量,都属于类而不属于对象,不能用对象来调用,只能用类名调用 //这不同于C++,是更合理的设计 //常量的值在类定义时就确定了,不因对象而不同,因此存放在类中更合理 class CNormclass { class CInclass { public float fx = 20; } public int _id; public const string cname = "CNormalclass"; //1,常量仅能修饰 :数字,bool,字符串,null引用 //不能像C++那样定义一个常量对象,这真是太悲哀了,因为很多时候这可以加速数据传递,增加安全性 //由于这个原因,C#的List.ToArray每次都只能返回一个内部数组的拷贝,因此使用list存储数量较大较复杂的数据时 //不要轻易使用ToArray,直接用List就行了,它也支持下标索引方式取数组元素 const CInclass lst = null; //2,readonly也不能实现常量对象的效果 //readonly仅表示变量本身不能被赋值,但不阻止通过对象变量更改对象内的字段 //onc.readonlyobj.fx = 20 public float fx = 20; private readonly CInclass readonlyobj = new CInclass(); public void FuncX() { } //3, 属性不能用readonly修饰 virtual public int ReadonlyProp {//4,属性可以为虚 private set; //可以加限定符 get; } public static void Test() { //1,不能调用非静态字段或方法 //this._id = 20; //error,没有this指针 //2,可以调用常量字段 var lname = cname; var onc = new CNormclass(); //私有变量在类的静态方法也可以访问 //2,虽然不能更改readonlyobj本身的值,却可以更改其内部成员的值,这就是readonly的作用 onc.readonlyobj.fx = 20; } } static class C712//类中类,默认为私有 {//静态类不能实例化,且只能声明:常量,静态常量,静态属性,静态方法 public const int constX = 20; //1,常量 public static int staticX = 0; //2,静态常量 public static int ix { set; get; } //3,静态属性 //一,【静态类中不能定义实例化字段】 //public int _id; //二,【静态类中不能定义实例化字段】 //void Ctest(){ //【error: 静态类中不能定义实例化方法】 // this._id = 20; //} static void Test()//4,静态方法 { //三,【静态方法中不能调用非静态变量或方法,因为没有this指针】 //_id = 20; //error //四,【可以调用常量字段,这与C++不同】 var c = constX; } } public const int ixd = 20; public static float fx = 20; public void Testff() { fx = 30; //等价于Program.fx = 30,而不是 this.fx = 30; Program.fx = 30; var tx = C712.constX; C712.staticX = 30; var ix = Program.ixd; //var oc7 = new C712(); //error 静态类不能创建实例 } #endregion #region 事件和委托 //-------------------------------------------------------------- //event -test //-------------------------------------------------------------- //使用event的好处,与delegate的区别: //event 本质上是一个委托,是做了一些安全措施的委托 //1,event 定义的委托只允许 +=操作,不允许=赋值,这样防止事件被误清空,delegate则没有这些限制 //2,event 定义的委托只能在本类中调用,可以防止外部触发,delegate没有这些限制 //3,不使用事件,delegate方式完全可以实现类似限制,通过私有变量和公有函数结合方式 class EventTest { public delegate void Delx(string s = ""); Delx _delegate; // 私有委托,防止外部调用 public event Delx _event; //公有事件,给外部通过+=注册使用,但_event()函数只能在本类调用,不能在类外调用 //------------------------------------------------------------- //1 ,委托方式 //------------------------------------------------------------- //(1)外部调用eventTest.AddListener(func)方式注册事件 public void AddListener(Delx callback) { _delegate += callback; } //(2)本类对象调用此函数触发事件 void DelegateBrocast() { _delegate("delegate"); //回调,触发事件 } //------------------------------------------------------------- //2,事件方式 //------------------------------------------------------------- //(1)外部使用 _event += 方式注册回调函数 //(2)本类对象调用此函数触发事件 void EventBrocast() { _event("event");//回调,触发事件 } } class Listener { public void OnEvent(string s) { WriteLine("on-event---------------" + s); } } static void TestEventAndDelegate() { Listener l1 = new Listener(); EventTest test = new EventTest(); //1,事件方式 test._event += l1.OnEvent; //注册事件 //test._event = l1.OnEvent; //编译错误,事件只能使用+=,防止事件被清空 //test._event("event"); //编译错误,事件不能在类外调用,事件只能由其所在类调用 //2,委托方式 test.AddListener(l1.OnEvent); //注册委托,通过函数对委托进行注册,因委托是私有的,可防止直接操作 test._delegate() } #endregion #region 文件和目录 static void FileAndDirectory() { //------------------------------------------------------------------------- //文件对象的相关操作 //------------------------------------------------------------------------- //方式一,使用工具类:File类,不需生成对象 var file = File.Open("f:/test.txt", FileMode.Create, FileAccess.ReadWrite); //方式二,通过FileStream的对象 var filestream = new FileStream("f:/test._txt", FileMode.Create, FileAccess.ReadWrite); //------------------------------------------------------------------------- //目录文件相关操作 //------------------------------------------------------------------------- //方式一,实例化DirectoryInfo类 var dir = new DirectoryInfo("f:/tolua"); //(1)获取目录 foreach (var d in dir.GetDirectories("*.*", SearchOption.AllDirectories)) { WriteLine(d.FullName); } //(2)获取文件 foreach (var fileinfo in dir.GetFiles("*.*", SearchOption.AllDirectories)) { WriteLine(fileinfo.FullName); } //方式二,使用工具类: Directory类,不需生成对象 //(1)获取目录 var dirs = Directory.GetDirectories("f:/tolua", "*.*", SearchOption.AllDirectories); //(2)获取文件 dirs = Directory.GetFiles("f:/tolua", "*.*", SearchOption.AllDirectories); for (int i = 0; i < dirs.Length; ++i) {//打印输出 WriteLine(dirs[i]); } } #endregion #endregion #region 2018.7.17 #region 计算机中浮点数的存储 static void TestFloat() { using (var ms = new MemoryStream()) { using (var br = new BinaryWriter(ms)) { br.Write(125.5f); var bytes = ms.GetBuffer(); } } unsafe { float fx = 125.5f; int* pfx = (int*)(&fx); } } #endregion #region 位移运算 static void TestBitShift() { //---------------------------------------------------------------------------- //十进制数转二进制: //1,原理:将数X右移1位,最低位被移出,再左移,得到了数X0,则x-x0即为最低位的值 //2,手工算法:根据1的原理,不断的对一个数整除2得余数,了终得到余数序列即是二进制的反向序列 //3,左移等价于乘2,右移等价于除2,原理是乘法的竖式算法, // 101 //x 010 //------- 竖式算法适用于任何进制的加减法和乘法运算 // 000 //+101 //------- // 1010 //---------------------------------------------------------------------------- int x = 7; List<Byte> bits = new List<Byte>(4); while (x != 0) { var left = x - ((x >> 1) << 1);//<=> x - x/2*2 bits.Add((byte)left); x = x >> 1; } } #endregion #region IEnumerableAndLinQ class Product { public int cateId; public string name; } class Category { public int id; public string name; } public static void TestIEnumerableAndLinq() { Category[] cates = new Category[] { new Category{id = 1, name = "水果"}, new Category{id = 2, name = "饮料"}, new Category{id = 3, name = "糕点"}, }; Product[] products = new Product[] { new Product{cateId=1, name = "apple"}, new Product{cateId=1, name = "banana"}, new Product{cateId=1, name = "pear/梨"}, new Product{cateId=1, name = "grape/葡萄"}, new Product{cateId=1, name = "pineapple/菠萝"}, new Product{cateId=1, name = "watermelon/西瓜"}, new Product{cateId=1, name = "lemon/柠檬"}, new Product{cateId=1, name = "mango/芒果"}, new Product{cateId=1, name = "strawberry/草莓"}, new Product{cateId=2, name = "bear/啤酒"}, new Product{cateId=2, name = "wine"}, new Product{cateId=3, name = "cake"}, new Product{cateId=3, name = "basicuit/饼干"}, }; var rets = cates.Where((x) => { return x.id > 1 && x.id < 5; }); var iter = rets.GetEnumerator(); while (iter.MoveNext()) { //WriteLine(iter.Current); } var set = from c in cates //这里只能写一个条件,就是equals,用来关联两个表 //并且 c相关的条件只能写在equals左边,p相关条件只能写equals右边 join p in products on c.id equals p.cateId //这里存放的是 products中的元素合集,而不是cates中的元素合集 //如果 from p in products join c in cates on c.id equals p.id into xgroups //则xgroups中放的是cates中的元素集合 //这里是说将products中cateId等于c.id的所有元素放入一个组xgroups中 into xgroups orderby c.id descending //对set中的结果进行降序排列 //where m > 4 && m < 10 //这里就可以写多个条件了 //from in 相当于外层循环,join in 相当于内层循环 //select在双层循环体中,每执行一次循环,【如果符合条件】,则执行一次结果选择 //双层循环完成后,最终将很多条选择提交给set //【注意,如果不符合条件 select不会执行】 select new { cate = c.name, grp = xgroups }; //可以生成一个新的对象 foreach (var p in set) { WriteLine("分组:" + p.cate); foreach (var g in p.grp) { WriteLine(g.cateId + "," + g.name); } } } #endregion #region 类和继承 class CTestX { public virtual void OnUpdate() { Console.WriteLine("base-on-update"); } public virtual void OnUpdate2() { Console.WriteLine("base-on-update2"); } public void Update() { this.OnUpdate(); //注释1,如果子类有overide则调用子类的,否则调用自己的 } public CTestX() { } protected CTestX(float fx) { WriteLine("CTestX"); } ~CTestX() { WriteLine("~Ctestx"); } public float fx; string name; } //子类不能访问基类任何私有的东西,包括方法,字段,属性,但它们都被继承了,属于子类,从实例内存可证 //方法包括构造函数,即当基类是私有构造函数时,子类无法在初始化列表中调用base()来初始化 class CTestChildX : CTestX { CTestX otestx; public CTestChildX() : base(1)//当基类为私有构造时,这里base无法调用 {//当基类没有无参构造函数时,必须在初始化列表中初始化所有成员对象,如otestx WriteLine("CTestChildX"); } //注意overide与virtual的区别: //1,overide : 表明【函数是对基类的重写】 且 【本身是虚函数可被子类重写】 //【函数会与基类、子类发生虚函数机制】 //2,virtual : 仅表明函数是个虚函数,不会与基类发生虚函数机制 //如果子类overide了该函数,则会与子类发生虚函数机制 //3,多级继承中只要有一级没override,虚函数机制就会打断在此层级,见 //override在编译层的机制是重写虚函数表中的函数地址 //即将继承而来的虚函数表中的虚函数地址替换成本类的虚函数地址 public static void TestDerive() { // CTestX ox = new CTestChildX(); // ox.OnUpdate(); //base-on-update,无虚函数机制发生 // ox.OnUpdate2(); //child-on-update2,虚函数机制发生 // ox = new CTestY(); // ox.OnUpdate(); //base-on-update,无虚函数机制发生 CTestChildX ocx = new CTestZ(); ocx.OnUpdate(); //grand-child-on-update } public override void OnUpdate() { Console.WriteLine("child-on-update"); } public override void OnUpdate2() { Console.WriteLine("child-on-update2"); } ~CTestChildX() //不支持virtual { WriteLine("~CTestChildX"); } } class CTestY : CTestChildX { public override void OnUpdate() { Console.WriteLine("grand-child-on-update"); } } class CTestZ : CTestY { //因为这里的Update不是虚函数,因此 public void OnUpdate() { Console.WriteLine("grand-grand-child-on-update"); } } struct CTX { void Test() {//不支持C++的const语法 } } //1,不能继承结构,可以实现接口, //2,不能有虚函数 struct CCTX //: CTX { public void Test() { } } #endregion #region 字符串格式化 static void TestStrFormat() { var str = Console.ReadLine(); while (str != "exit") { int ix; Int32.TryParse(str, out ix); //ix = 120 var f1 = string.Format("{0 :d5}", ix); //"00120" var f2 = string.Format("{0,-10:d5}", ix);//"00120 " var f3 = string.Format("{0:x}", ix); //16进制输出到字符串 var f4 = string.Format("{0:0.000}", ix);//浮点数 120.000 Console.WriteLine("-----------begin-------------"); Console.WriteLine(f1); Console.WriteLine(f2); Console.WriteLine(f3); Console.WriteLine(f4); Console.WriteLine("------------end-------------"); str = Console.ReadLine(); } } #endregion #endregion #region 2018.7.25 #region 引用返回值(不是右值引用) static int[] _bookNum = new int[] { 1, 2, 3, 4, 5, 6 }; static ref int GetBookNumber(int i) { int x = 10; return ref _bookNum[i]; } static void TestRefReturn() { ref int rfn = ref GetBookNumber(1); rfn = 10101; //_bookNum[1]变成了 10101 int vn = GetBookNumber(2); vn = 202; //_bookNum[2]未变,仍为3 ref int x = ref vn; } #endregion #region 索引器 class mylist<T> { const int defaultCap = 4; T[] items; int count; int cap = defaultCap; public mylist(int cap = defaultCap) { if (cap != defaultCap) this.cap = cap; items = new T[cap]; } public T this[int idx] { set { items[idx] = value; } get { return items[idx]; } } } enum Color { red, green, blue, yellow, cyan, purple, black, white, } static void TestIndexer(Color clr = Color.black) { mylist<string> lst = new mylist<string>(); lst[1] = "hello"; } #endregion #region 部分类 //部分类的作用是可以把一个庞大的类拆分到多个文件,每个文件实现一部分 //而不是实现像C++那样将声明与实现分开 //若要实现声明(接口)与实现分开,应该使用抽象类或接口 partial class CPartclass { public void ShowName() { WriteLine("show name"); } } partial class CPartclass { public void ShowAge(){ WriteLine("show age"); } } static void TestPartclass() { CPartclass opc = new CPartclass(); opc.ShowName(); opc.ShowAge(); } #endregion #region 动态分配对象数组C#与C++的区别 struct xobject { public float fx, fy, fz; //全是public的 } static void TestDynamicAllocInCSharpCpp() { //1,对于引用类型数组,需要两步才能完成,因为数组中存放的是对象的引用 //1.1 c#中 xobject[] arr = new xobject[2];//这时候,只是分配了一个引用数组,arr[0],arr[1]均为null for (int i = 0; i < 2 ; i++) { arr[i] = new xobject(); //为数组中每个引用申请对象 } //1.2 c++中 //xobject** pp = new xobject*[2]; //pp[0] = new xobject(); //pp[1] = new xobject(); //2 对于值类型数组,则只需一步,因为数组中放的就是值,这在C#与CPP中都一样 //2.1 C#中 int[] iarr = new int[2]; var a0 = iarr[0]; //0 var a1 = iarr[1]; //0 xobject[] varr = new xobject[3]; varr[0].fx = 0.1f; varr[1].fy = 2.5f; varr[2].fz = 12; //2.2,在C++中 //xobject* pobjs = new xobject[2]; //每个数组元素都是一个值类型对象 //pobjs[0].fx = 20; } #endregion #region Object?语法 static void TestobjAsk() { object obj = "hello"; WriteLine(obj?.ToString());//如果obj不为null则调用tostring } #endregion #region C#默认字符编码及系统默认编码 //默认编码为unicode,字符串本身的编码并不重要,字节读写时指定的编码才重要,如下面的BinaryWriter //Encoding.Default是当前系统的默认编码,并不是c#字符串的默认编码 //Encoding.Default规则:汉字2字节,其它1字节 static void TestDefaultStrEncoding() { string str = "hdd好"; using (var ms = new MemoryStream()) { using (var br = new BinaryWriter(ms, Encoding.Default)) { br.Write(str); var len = ms.Length-1; WriteLine(len); } } } #endregion #region 属性attribute和反射 class ReflectableClass { public float fx; public string str; //static const int x = 20; //这在C++中是可以的 public void Printstr(string str, int idx) { WriteLine(str + ":" + idx); } } static void TestReflect() { ReflectableClass ox = new ReflectableClass(); Type t = typeof(ReflectableClass);//Type.GetType("ConsoleApplication1.Program.ReflectableClass");//ox.GetType(); var tname = t.GetField("name"); var tfx = t.GetField("fx"); var func = t.GetMethod("Printstr", new Type[] {typeof(string),typeof(int) }); func.Invoke(ox, new object[] { "helloworld", 1 }); Type Ts = Type.GetType("System.String"); var fs = Ts.GetMethod("Substring", new Type[] { typeof(int), typeof(int) }); var subs = fs.Invoke("hello world", new object[] { 1, 5 }); WriteLine(subs); } static void TestAttribute() { } #endregion #endregion #region 2018.7.30 #region 扩展方法测试 static void TestExtMethod() { ExtTargetCls oet = new ExtTargetCls(); oet.methodExt(100); WriteLine(oet.sum); } #endregion #region 元组:同时传递多个不同类型参数 //作用时,可以很方便的,高效的返回一组不同类型的值或对象 //因为是泛型,所以高效 //但是它最多只有8个参数,也就是说不能当作ArrayObject的替代品 static void TestTuple() { Tuple<int, float> tupleFunx() { return new Tuple<int, float>(1, 2); } var tp = tupleFunx(); WriteLine(tp.Item1); WriteLine(tp.Item2); } #endregion #region 数组排序:实现IComparable和传递排序函数 //注意,List排序也是这样的,因为它本身就是一个数组 class ComparableObj<T> : IComparable { public T elem; public ComparableObj(T fx) { elem = fx; } public int CompareTo(object obj) { var objc = (dynamic)(ComparableObj<T>)obj; if (elem == objc.elem) return 0; else if (elem < objc.elem) return -1; return 1; } } static void TestCompareableobj() { var rand = new Random(); ComparableObj<float>[] arrf = new ComparableObj<float>[10]; for (var i = 0; i < 10; ++i) { arrf[i] = new ComparableObj<float>(rand.Next(1, 100)); Write(arrf[i].elem + " "); } WriteLine(); //方式一,实现了IComparable,用它来排序,升序 Array.Sort(arrf); foreach (var a in arrf) { Write(a.elem + " "); } WriteLine(); //方式二,传递一个排序函数,使用它来排序,降序 Array.Sort(arrf, (a, b) => { if (a.elem == b.elem) return 0; else if (a.elem < b.elem) return 1; return -1; }); foreach (var a in arrf) { Write(a.elem + " "); } WriteLine(); } #endregion #region 只读集合 void TestReadonlySet() { var lst = new List<int>(); var rdlst = lst.AsReadOnly(); //生成一个包装类,引用原来的lst,因此是高效的 //rdlst[0] = 2; //error, read only var llst = new LinkedList<int>();//这个才是链表,而list就像是c++的vector } #endregion #endregion #region 2018.7.31 #region JSON void TestJson() { } #endregion #region CPP与CS间数据传递转换 #endregion #region 线程 static void TestThread() { //Thread.Yield(); Thread t1 = new Thread(() => { int i = 0; while (i++ < 25) { Thread.Sleep(300); WriteLine("T1>> " + i); } }); Thread t2 = new Thread(() => { //t1先执行(<=1000毫秒),t2等待 t1.Join(1000); //t1,t2同时执行,若上一步t1已完成则不执行 int i = 0; while (i++ < 10) { Thread.Sleep(300); WriteLine("T2>> " + i); } //若t1还活着,继续执行 //t2是前台线程,main函数会等待t2的结束 t1.Join(); }); t1.Start(); t2.Start(); t1.IsBackground = true; t2.IsBackground = true; //t1.IsBackground = true; //t2.Join(); Thread.Sleep(2000); WriteLine("main-thread-end"); } #endregion #region 线程池 void TestThreadPool() { } #endregion #region 任务 static void TestTask() { WriteLine("TestTask: " + Thread.CurrentThread.ManagedThreadId); //任务开启方式一,实例实现 var task = new Task(() => { WriteLine("task: " + Task.CurrentId + "," + Thread.CurrentThread.ManagedThreadId); }); task.Start(); task.Wait(); //等待方式一 Task.WaitAny(task); //等待方式二 //任务开启方式二,静态方法实现 var t1 = Task<string>.Run(delegate //Task<string>中的string表示返回值类型,也可不写,由模板自动推导 { WriteLine("task1: " + Task.CurrentId + "," + Thread.CurrentThread.ManagedThreadId); Thread.Sleep(2000); return "suceed"; //返回值类型,对应Task<string>中的string,如果类型写错也没关系 }); t1.Wait(); //等待任务完成,因为是在主线程中调用的,因此是让主线程等待任务完成,不写的话主线程直接结束了 WriteLine("线程1执行结果:" + t1.Result); //suceed } #endregion #region 程序集 #endregion #region 多线程调试 #endregion #region 委托综合使用小例子 static void delegateTestx0 (){ void ifunc(int x, Func<int, int> dx) { WriteLine(dx(2)); } var lst = new List<int>() { 1, 2, 3 }; foreach (var v in lst) { ifunc(1, delegate (int x) {//像lua的回调函数那样使用 return v; //闭包中的v }); ifunc(1, (x) => { return v; }); } } #endregion #region 异步 async await public static async void AsyncFunc() { WriteLine(Thread.CurrentThread.ManagedThreadId); //主线程 var task = Task.Run(() => { for(int i= 0; i<10; ++i) { WriteLine(Thread.CurrentThread.ManagedThreadId + ":task>>" + i); //线程1 Thread.Sleep(100); } }); var task1 = Task.Run(() => { for (int i = 0; i < 10; ++i) { WriteLine(Thread.CurrentThread.ManagedThreadId + ":task1>>" + i);//线程2 Thread.Sleep(100); } }); await task; //等待线程1完成 await task1;//等待线程2完成 WriteLine("task and task1 finished"); var task2 = Task.Run(() => { for (int i = 0; i < 10; ++i) { WriteLine(Thread.CurrentThread.ManagedThreadId + ":task2>>" + i);//线程3 Thread.Sleep(100); } }); await task2;//等待线程3完成 Task.WaitAll(task, task1, task2); //无效,因为代码执行到这里时主线程已结束 WriteLine("---------------------------------------------------"); } public static void AsyncFunc2() { WriteLine(Thread.CurrentThread.ManagedThreadId); //主线程 var task = Task.Run(() => { for (int i = 0; i < 10; ++i) { WriteLine(Thread.CurrentThread.ManagedThreadId + ":task>>" + i); //线程1 Thread.Sleep(100); } }); var task1 = Task.Run(() => { for (int i = 0; i < 10; ++i) { WriteLine(Thread.CurrentThread.ManagedThreadId + ":task1>>" + i);//线程2 Thread.Sleep(100); } }); task.Wait();//等待线程1完成 task1.Wait();//等待线程2完成 WriteLine("task and task1 finished"); var task2 = Task.Run(() => { for (int i = 0; i < 10; ++i) { WriteLine(Thread.CurrentThread.ManagedThreadId + ":task2>>" + i);//线程3 Thread.Sleep(100); } }); task2.Wait();//等待线程3完成 WriteLine("---------------------------------------------------"); } //----------------------------------------------------------------------- //异步方式实现多个任务(线程)间的并发执行与顺序执行 //一个任务就是一个线程 //await task 与task.wait的区别: //task.wait会阻住当前线程,直到task执行完成,而await不会,它只表明了当前在任务会在task之后执行 //----------------------------------------------------------------------- //方式一,采用 async-await方式实现 //这种方式写起来较优雅,但较抽象,且不知道所有任务的结束点,Task.waitAll对此无效 static void TestAsyncAwait() { WriteLine(Thread.CurrentThread.ManagedThreadId); //主线程 AsyncFunc(); WriteLine("after asyncfunc"); //必须这样等待任务结束,因为AsyncFunc中的Task.WaitAll无效 //或者将每个任务都设置为前台线程 Thread.Sleep(3000); } //方式一,采用不使用 async-await关键词,直接使用Task的基本功能来实现 //这种方式更直观,易于理解,且利于控制 static void TestAsyncWait() { WriteLine(Thread.CurrentThread.ManagedThreadId); //主线程 var task = Task.Run((Action)AsyncFunc2); WriteLine("after asyncfunc"); task.Wait(); } #endregion #region 正则表达式 #region 贪婪匹配和最少匹配 #endregion #region 分组 #endregion #endregion #region 正则在字符串中的使用 static void TestRegexInStr() { var st = "hello;world;a"; var mt = Regex.Match(st, ";world;"); var ret = mt.Result(".");//??? // string pattern = "--(.+?)--"; // string replacement = "($1)"; // string input = "He said--decisively--that the time--whatever time it was--had come."; // foreach (Match match in Regex.Matches(input, pattern)) // { // string result = match.Result(replacement); // Console.WriteLine(result); // } } #endregion #endregion #region 2018.8.1 #region 异步调用Invoke delegate void MyTakeAwhileDelegate(int x, int time); static MyTakeAwhileDelegate a = invokefunc; static void invokefunc(int x, int time) { WriteLine("begin invoke: " + Thread.CurrentThread.ManagedThreadId); //var str = Console.ReadLine(); //WriteLine(">>" + str); Thread.Sleep(time); } static void TestInvoke() { // var iar = a.BeginInvoke(delegate(IAsyncResult ar) { // WriteLine("complete: " + ar.IsCompleted); // WriteLine("end invoke: " + Thread.CurrentThread.ManagedThreadId); // TestInvoke(); // // }, null); //【线程的顺序执行模式】 //多个线程对同一函数进行顺序访问,不需要考虑线程同步问题,也不需要waitone等操作 //不管系统会使用多少个线程来处理工作,但同时只有一个在执行,EndInvoke保证了这一点 //这种模式在网游客户端中很有用,客户端只需要2个线程:一个主线程用于处理游戏逻辑与显示画面 //另一个线程则用于与后端进行网络通讯,这个线程就只需要使用【线程的顺序执行模式】 //来循环处理网络消息:读取网络消息,阻塞等待读取完成,然后再读取网络消息,阻塞等待读取完成... while (true) { //注意这里的参数与委托对应,而且多了两个:callback, obj是系统加的 var ar = a.BeginInvoke(1, 1000, null, null); a.EndInvoke(ar); //阻塞,只到线程执行完线程函数 } //a.EndInvoke(iar); //iar.AsyncWaitHandle.WaitOne(); } #endregion #region 初始化器 class CInitclass { public CInitclass() { } public CInitclass(string name, int age) { this.name = name; this.age = age; } public string name; public int age; } static void TestInitial() { var oc = new CInitclass { name = "jim", age = 14 }; var oc1 = new CInitclass() { name = "jim", age = 14 }; var oc2 = new CInitclass("tim", 13) { name = "jim", age = 14 }; var oc3 = new { name = "jim", age = 14, sex = 1 }; //匿名对象 int[] arr = { 1, 2, 3 }; int[] arr2 = new int[] { 1, 2, 3 }; List<int> lst = new List<int> { 1, 2, 3 }; List<int> lst1 = new List<int>(10) { 1, 2, 3 }; //capacity = 10 lst1.Capacity = 5; WriteLine(lst1.Capacity); lst1.ForEach((i) => WriteLine(i)); var dict = new Dictionary<int, string> { { 1, "a" }, { 2, "b" }, { 3, "c" } }; var dict1 = new Dictionary<int, string>() { { 1, "a" }, { 2, "b" }, { 3, "c" } }; } #endregion #region 协变和逆变 //协变发生在数组,模板,委托上, //父子类之间的转换不是协变,不是逆变 //转变的前提是元素类型有父子关系,如 class A{}; class B : A{}; B b; A a; //若子群可以转为父群,则称为协变,如 A[] a = new B[10] //协变必须是在引用类型之间,值与引用类型之间是不能协变的,如 object[]和 int[] //虽然 object是int的父类,但 int 是值类型 //再如 object[] strs = new string[10]是可以的,因为 object就string的父类且二者都是引用类型 //====================================================================== //总结:协变和逆变只是父子对象间转换规则在模板,委托,数组上的表现 //本质上还是子对象转父对象,没有父对象转子对象的现象存在 //模板类中的协变与逆变转换过程较为抽象,难时一眼看出,解析方法是:用实际生成的对象去调用,在调用过程中分析 //如下面的二例:【泛型委托中的协变逆变】和【泛型接口中的协变逆变】 #region 普通协变逆变 class tshape<T> { } class tcircle<T> : tshape<T> { } static void xiebianx(CTestX[] array) { array = new CTestChildX[10]; } static void TestXiebianNibian() { object[] ocs = new CNormclass[10]; object[] strs = new string[10]; //协变的一个陷阱,编译时正常,运行时抛出异常: 类型不匹配 strs[0] = 10; //泛型类本身的协变(有父子关系,泛型参数相同) tshape<int>[] tsps = new tshape<int>[10]; tshape<CNormclass>[] tcs = new tcircle<CNormclass>[10]; //通过函数参数测试普通类的转变 CTestX[] ox = new CTestX[10]; xiebianx(ox); } #endregion #region 委托中的协变逆变 class XIEBIAN { delegate CTestX[] ArrDelegate(); CTestChildX[] func001() { return new CTestChildX[10]; } delegate CTestX JustDelegate(); CTestChildX func002() { return new CTestChildX(); } void TEst() { ArrDelegate ad = func001; JustDelegate dd = func002; } } #endregion #region 泛型委托中的协变逆变 delegate void FuncPtrin<in T>(T ox);//这里的in仅用来限制T类型,说明T只可用于输入参数,而不能用于输出。in与协变逆变无关。可以去除 void testfuncptr1(CTestX ox) { } delegate T FuncPtrout<out T>();//out限制T只能用于输出参数,即返回值。与协变逆变无关。可去除 CTestX testfuncptr2() { return new CTestChildX(); } void testfuncptr() { //泛型委托的协变比较抽象,其实,从它的【调用原理】来思考就很容易了 FuncPtrin<CTestChildX> p1 = testfuncptr1; FuncPtrin<CTestX> p2 = testfuncptr1; //【调用原理】: //1,p1的实参必须是T类型 //2,p1的实参必须能传入它指向的函数中 p1(new CTestChildX()); p2(new CTestChildX()); p2(new CTestX()); FuncPtrout<CTestX> p3 = testfuncptr2; CTestX otx = p3(); //----------------------------------------------------------------------- //其实这里不存在什么所谓的逆变,只有一种规则:子对象可以转为父对象 //只要让参数接收者和返回值接收者都是父对象,就可以了 //----------------------------------------------------------------------- } #endregion #region 泛型接口中的协变逆变 class CAnimal { } class CDog : CAnimal { } interface ISpeak<in T, out T2>//这里的in和out就是协变逆变的关键了,没有它们编译器不知道如何进行父子关系转换 { T2 PrintInfo(T o); float fx { set; get; } } class Speaker<T, T2> : ISpeak<T, T2> where T2 : new() //必须有公有无参构造函数(或默认构造函数) { public float fx { set; get; } public T2 PrintInfo(T o) { return new T2(); } } void Test003() { ISpeak<CDog, CAnimal> speaker = new Speaker<CAnimal, CDog>(); speaker.PrintInfo(new CDog()); } #endregion #endregion #region 2018.8.2 #region 相等比较 static void TestVarEquals() { //-------------------------------------------------------------- //C#中字符串都是常量,但在底层实现上还是C++的方式,分为常量字符串与变量字符串 //1,以数组形式给出的字符串是变量字符串 //2,以字符串形式给出的是常量字符串 //每个变量字符串都有不同的地址,而一个常量字符串只有一个地址,它是全局的 //如下sa, sb指向两个地址不同而内容相同的字符串,sa1,sb1都指向同一个常量字符串"hello" string sa = new string(new char[] { 'h', 'e', 'l', 'l', 'o' }); //变量字符串 string sb = new string(new char[] { 'h', 'e', 'l', 'l', 'o' });//变量字符串 string sa1 = "hello";//常量字符串 string sb1 = "hello";//常量字符串 WriteLine(sa.GetHashCode() + "," + sb.GetHashCode()); WriteLine(sa.Equals(sb));//true,调用string.equals(string) WriteLine(sa == sb);//true,string的operator == object oa = sa; object ob = sb; object oa1 = sa1; object ob1 = sb1; WriteLine(oa.Equals(ob));//true, 多态调用,实际调用的是string.Equals(object) WriteLine(oa1.Equals(ob1));//true, 多态调用,实际调用的是string.Equals(object) //运行时,打印sa,sb, sa1, sb1的地址,可以看到sa,sb中存放的地址不同,sa1,sb1中存放的地址相同 // &sa // 0x000000000028ecb0 // * &sa: 0x0000000002472ed8 // & sb // 0x000000000028eca8 // * &sb: 0x0000000002472f70 // & sa1 // 0x000000000028eca0 // * &sa1: 0x0000000002472dc0 // & sb1 // 0x000000000028ec98 // * &sb1: 0x0000000002472dc0 WriteLine("ref equal : " + ReferenceEquals(sa, sb)); WriteLine("ref equal : " + ReferenceEquals(sa1, sb1)); WriteLine("oa == ob: " + (oa == ob)); //false,oa,ob中存放的地址不同 WriteLine("oa1==ob1: " + (oa1 == ob1)); //true,oa1,ob1中存放的地址相同,都是常量字符串hello的地址 object oc = new object(); object od = new object(); WriteLine(oc.Equals(od)); //false, object.equals(object) WriteLine(oc == od);//false //如果没有实现重写,对于引用类型,那么原始的object.equals()与 ==没有任何区别,二者总能得到一样的结果 //因为引用类型其实是一个指针,==比较的是指针的值,也就是地址,equals比较的也是地址。 //string类重写了==和equals,实现了字符串内容的比较,而非地址的比较。 object o1 = new CNormclass(); object o2 = new CNormclass(); WriteLine(o1.Equals(o2)); //false, 多态调用, CDefOveride.Equals(object) int ia = 12; short isa = 12; WriteLine(ia.Equals(isa)); // true, short可以转为int,故多态调用Int32.Equals(Int32 obj) WriteLine(isa.Equals(ia)); // false, int不能直接转为short,故多态调用Int16.Equals(object obj) } #endregion #endregion #region 2018.8.3 #region 线程同步 #region 同步事件和等待句柄 //https://docs.microsoft.com/zh-cn/dotnet/csharp/programming-guide/concepts/threading/thread-synchronization static void TestWaitHandle() { //自动重置事件 //一次只能激活一个线程,因为一旦激活后信号被自动置为了false var autoEvent = new AutoResetEvent(false); void tfunc(object o) { WriteLine("worker thread " + (int)o + " started, now waiting on some event ... "); autoEvent.WaitOne(); WriteLine("worker thread " + (int)o + " reactivated, now exiting..."); } var threads = new Stack<Thread>(); WriteLine("输入创建 的线程数"); var num = 1; while (!(int.TryParse(ReadLine(), out num))){} for(int i=0; i < num; ++i) { var t = new Thread(tfunc); t.Start(i); threads.Push(t); Thread.Sleep(20); } Thread.Sleep(1000); while(threads.Count > 0) { ReadKey(); autoEvent.Set(); //发出信号,设置信号为true,一旦有线程被激活后,信息就被设置为了false threads.Pop(); } } #endregion #region 一个线程终止另一个线程及信息传递异常捕获 class CThreadAbortInfo { public string info; public CThreadAbortInfo(string s) { info = s; } } static void TestThreadAbort() { var t1 = new Thread(() => { WriteLine("t1 started"); try { int i = 0; while (true) { Write("."); Thread.Sleep(200); i = i / 0; } } catch (DivideByZeroException e)//如果不处理,则系统会自己处理 { //throw; //让程序引发异常,如果不写,则程序正常运行,因为异常被丢弃了 } catch (ThreadAbortException ex)//如果不处理,程序正常运行 { var info = ex.ExceptionState as CThreadAbortInfo; if (info != null) { WriteLine(info.info); } } }); t1.Start(); var t2 = new Thread(() => { Thread.Sleep(1000); //调用这个函数,会抛出异常,但若不去捕获,程序就什么都不会发生 //抛出异常与显示异常是不同的 t1.Abort(new CThreadAbortInfo("t1 is terminated by thread t2")); }); t2.Start(); } #endregion #endregion #region as和引用类型转换本质 void Refcasttype() { //注意,不论是as转换还是强制转换都是在指针转换,而不是对象转换,遵守C++的规则:子类可以转父类 //C#中,父类也可以转子类,因为它们都是指针,但若实际类型不符合则结果为空 var o = new CNormclass(); var t = o as IDisposable; var ot = new CTestX(); var ot2 = new CTestChildX(); WriteLine("as1: " + ((CTestChildX)ot)); WriteLine("as1: " + (ot as CTestChildX)); WriteLine("as3: " + (ot2 as CTestX)); WriteLine("as4: " + ((CTestChildX)ot2)); using (ot as IDisposable)//判断如果它实现了该接口 { } } #endregion #region 多播委托 delegate void MDX(); static void TestMultiDelegate() { void func1() { WriteLine("func1"); } void func2() { WriteLine("func2"); } void func3() { WriteLine("func3"); } MDX md = func1; //【!】第一步不能写 +=,因为它还没有初始值 md += func3; md += func2; md(); //func1 func3 func2 执行顺序与添加顺序相同 md -= func1; md(); //func3 func2 //md -= (func2 + func3); //wrong md = func1; //ok,事件不允许这样 md(); md -= func1; //编译运行都OK,调用出错 md -= func2; //编译运行都OK,调用出错 md();//调用异常 } #endregion #region UDP通信 static void TestUDp() { var ipenda = new IPEndPoint(IPAddress.Parse("127.0.0.1"), 11000); var ipendb = new IPEndPoint(IPAddress.Parse("127.0.0.1"), 12000); void StartUdpClientA() { UdpClient udp = new UdpClient(ipenda); //udp.Connect(ipendb); while (true) { var recvBytes = udp.Receive(ref ipendb); var bytes = Encoding.ASCII.GetBytes("信息已收到[" + recvBytes.Length + "],请继续发送"); Thread.Sleep(1000); udp.Send(bytes, bytes.Length, ipendb); } } void StartUdpClientB() { UdpClient udp = new UdpClient(ipendb); //udp.Connect(ipend); while (true) { WriteLine("请输入发信息:"); var str = ReadLine(); var bytes = Encoding.ASCII.GetBytes(str); udp.Send(bytes, bytes.Length, ipenda); WriteLine("信息已发送等待回复:"); var recvBytes = udp.Receive(ref ipenda); WriteLine(">>收到回复,字节数:" + recvBytes.Length); } } var t1 = new Thread(StartUdpClientA); var t2 = new Thread(StartUdpClientB); t1.Start(); t2.Start(); } #region TCP通信 #endregion #endregion #region 可空类型 void TestNullabletype() { //可空类型是一个泛型结构体 Nullable<int> ix0 = null;//等同于下式 int? ix = null; //可空类型 object oa = 5; int iy = ix ?? 7; //7 object ob = oa ?? 10; //5 WriteLine(iy); WriteLine(ob); } #endregion #endregion #region 2018.8.4 #region 抽象类与接口对比 interface ixinterface//不能加sealed { //1, 接口中不能写public,因为默认为public,C#不会存在可有可无的东西 //2,接口可以有抽象 int this[int x] { set;get; } //3,接口中不可以写实例化字段和属性 //4,可以有事件 } abstract class AbstractClass//不能加sealed { //int this[int x] { set; get; } int ix { set; get; } public abstract int iy { set; get; } void Func() { } int this[int x] {//可以定义索引器,但必须实现 set { } } } class Dabclass : AbstractClass { public override int iy { get => throw new NotImplementedException(); set => throw new NotImplementedException(); } } #endregion #endregion #region C#的析构函数不同于C++ class myTemp<T> { public myTemp(){} /*private virtual */~myTemp(){//析构函数不能带任何限制声明,如public, protected, private, } } class Tehua : myTemp<string>{} #endregion #region 2018.8.14 static void testIntLimit() { var le = -2147483648 < 2147483648; int ix = -2147483648; WriteLine(ix - 1); //0x80000000 + 0xffffffff = 0x7fffffff } #endregion #endregion // ctrl + w, t 可以察看所有待做任务 static void Main(string[] args) { //TestVarEquals(); //TestUDp(); //TestWaitHandle(); //TestThreadAbort(); //TestMultiDelegate(); //TestVarEquals(); //TestInitial(); //TestInvoke(); //Thread.Sleep(30000); //TestXiebianNibian(); //TestAsyncWait(); //TestTask(); //TestThread(); //TestRegexInStr(); //TestCompareableobj(); //TestExtMethod(); //TestReflect(); //TestDefaultStrEncoding(); //TestDynamicAllocInCSharpCpp(); //TestPartclass(); //TestRefReturn(); //TestOperatorOverload(); // CTestChildX.TestDerive(); //TestFloat(); //var arr = returnArray(); } } #region 扩展方法 sealed class ExtTargetCls { public float sum = 10; } //扩展方法必须在顶级静态类中定义,不能是内部类 //能不能通过扩展方法来修改类库以达到不法目的? 不能,因为扩展方法只能修改类的公有成员 static class ExtentMethod { public static void methodExt(this ExtTargetCls target, float add) { target.sum += add; } } #endregion }