• 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('');
      65             string s3 = "hello/0/0dddddd".TrimStart('');//!!!!!!!!!!!!!!!!!!!!!!!!!!!!    
      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";
     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和的区别,如 s1="h0ello", s2 = "hello"
     166             //s2中的是字符串结尾符,除了C#不把它作为结束符外,其它语言都把它作为结束符,如U3D,LUA,C/C++等
     167             //而s1中的0仅是一个字符0而已,字符0的ASCII值是0X31=49,''的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 += '' + ",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 }
  • 相关阅读:
    mp3播放时间
    图片生成视频
    语音合成服务
    360p以上
    实现文字转语音功能
    字幕格式
    音频格式
    视频格式
    微信发朋友圈 -- 群营销素材同步
    FourCC
  • 原文地址:https://www.cnblogs.com/timeObjserver/p/9391470.html
Copyright © 2020-2023  润新知