数往知来C#之 String 集合 深拷与浅拷 序列化<五>
C# 基础常用string方法篇
复习、
1、引用类型与值类型
--》文件的复制与快捷方式的复制
2、垃圾回收
3、静态与非静态
--》如何定义静态成员与静态类
--》如何调用静态成员
4、异常
-》托管与非托管
-》.net frameworl
主要分为三个东西:CLR、CTS、类库
-》由CLR管理的代码就是托管的,否则就是费托管的(如File.Create)
->异常是一个类,需要抛出一个异常就要new
-》thorw关键字,抛出
二、常用类库--String
大写的String与小写的string有什么不同?
-》可以说两个都一样
-》String是一个类
-》构造方法 new string(char [] shs)
2)两个常用的属性(Length/Empty(常量))
-》length 这一点与数组的用法类似
-》str[0]这个中括号就不叫下标,叫索引
常用的方法(索引的使用、ToCharArrray)
不可变性
既然是不可变的,那么多个字符串,结果相同的时候,就没有必要每次生成一个对象(节省资源)
3)字符串拘留池(暂存池)
由于有字符串拘留池的存在,如果代码中有大量的字符串的值相同,那么他们都指向同一个对象,
整个代码中只创建一个对象。
字符串的内部实现与C语言的字符串的实现一样,可以参考 IL Assembly这本书
4)字符串的处理常用的
-》比较
1)Equals方法
-》静态的Equals方法
-》非静态的(string的/object的)
Equels方法法有三个重载,StringComparison是个枚举 OrdinalIgnoreCase表示不区分大小写比较。
// Equals方法
// -> 静态的比较方法(静态方法由类名点出来的)
string str1="abcd"; string str2="ABCD"; if (string.Equals(str1, str2, StringComparison.OrdinalIgnoreCase)) { Console.WriteLine("相等"); } else { Console.WriteLine("不等"); } // -> 非静态(string的,object的)(实例方法是有对象点出来的) if (str1.Equals(str2, StringComparison.OrdinalIgnoreCase)) { Console.WriteLine("相等"); } else { Console.WriteLine("不等"); }
string.Compare方法是静态方法,比较两个字符串大小,比较的的是没一位字符的unicode码,
比较规则:分别从两个字符串的第一位两两相比,如果第一位相等就再比较第二位,知道比出大的为止。
注意:如“22”和“123”相比,此时比较第一个‘2’比‘1’大,那么就后面的不会再比较了,此时“22”比“123”大
string str1="12"; string str2="23"; if (string.Compare(str1, str2) > 0) { Console.WriteLine("大于"); } else { Console.WriteLine("小于"); }
-》修整
Trim()方法,修整字符传收尾的空格,,并将新的字符串返回
Trim(praram char [] chs),去除字符数组中出现的字符
TrimEnd() 去掉右边的
TrimStart() 去掉左边的
-》检索
增加:
移除
查找
contains()
indexof()
lastindexof()
-》分割与合并
-》格式化
三、StringBuilder
->Appand() 追加
-》AppandLine 追加换行 相当于 Appand(“123\n”)
->AppandFramart 格式化
StringBuilder输出的时候记得要ToString()一下
C# 集合篇
1)string.format 格式化字符串(就是挖坑填坑)
可以参考console.writeline(“{0}{1}”,str1,str2);不同的是conbsole.writeline()是输出,Format是把它作为一个字符串返回。
-》@符号的两个好处:1)可以取消转义
2)写字符串的时候可以换行
二、集合
为什么要有集合?
数组不是动态的,他的长度动态的变动,为了拟补这些就有了集合
可以认为,集合就是一个动态的数组,并且提供了一些处理的方法
1)增、
ArrayList里面可以存放任何类型的数据,里面存放的数据类型是object的 (arraylist要引入命名空间 shift+alt+F10)
arrayList.Add(要添加的数据); (这是个实例方法)
arrayList.AddRange(数组); 这里要注意Add方法也可以放一个数组进去,但是不同的是Add方法是把整个数组当成一个集合的元素存到集合里去,
而AddRange方法是把数组的每个元素排列放到集合里去。
2)删、
Remove() 把指定的元素删除,如果集合里没有这个元素就会忽略掉,不会报异常
RemoveAt() 把集合里指定下标的元素删除,如果这个下标超出集合的长度就会报异常
3)改、
和数组一样通过下标改。直接赋值
4)查
contains(要查找的元素)
如果集合里有这个元素会返回一个true,否则返回false
5)插入
arrayList.Insert(要插入位置的索引,要插入的数据);
arraylist最大缺点就是Arrayliat是object类型,处理数据需要考虑类型兼容
需要把集合里的数据拿出来的时候需要强制转换,由于是object类型存什么类型的数据都可以,当集合里存的数据多的时候
我们强制转换就会就会比较麻烦,为了解决这个问题,List<>泛型集合就出现了。
2)Hashtable<键,值>
-》Hashtable的出现就是为了让我们便于查找集合里的数据的,
-》hashtable里面存的是一个 键 和 值。他们都是object类型的,可以存放任何类型的。
-》hashtable的键就相当于一个标记,要查找的时候可以通过 hashtable.Contains(要查找的键);方法就可以快速的找到这个键对应着的值(它是通过一个哈希散列算法来实现的)
I、增
hashtable.Add(键,值); //hashtable只有一个增加的方法,里面可以放任何类型的数据。
II、删
Remove(键);
-》我们知道ArrayList是通过下标来删除的,那么 我们的hashtable也是相同的原理,hashtable的键就相当于arraylist的下标,
如果把键改成0、1、2、3、4、5........那么就相当于一个ArrayList,那么可以得到结论,hashtable[键] 通过中括号 键来删除。
三、泛型集合
List<T> 这个尖括号里写什么类型这个集合就存什么类型的数据
-》list就相当于一个规定好存放类型的ArrayList,
->当我们规定好类型后就可以解决类型兼容的问题了,从几何里取数据时就方便多了。
List的增、删、改、查方法跟ArrayList一样。
Dictionary<键,值>
->Dictionary对应的就是hashtable,我们可以规定好键和值的类型,使用起来更加的方便
-》遍历的三种方法
foreach (string item in dic.Keys) //通过键来遍历 { Console.WriteLine(item); Console.WriteLine(dic[item]); } foreach (Person item in dic.Values) //通过值来遍历 { Console.WriteLine(item); } foreach (KeyValuePair<string,Person> item in dic) //我们有时候遍历的时候要同时用到集合的键和值,就可以通过这个方法 { //KeyValuePair也是一个集合 Console.WriteLine(item.Key + " "+item.Value); }
遍历集合用foreach, ArrayList和hashtable使用要引入命名空间,而List和Dictionary不用,可以看出微软提倡我们用 List和Dictionary
二、foreach (可以遍历任何实现了IEnumerble接口的对象)
foreach(临时数据类型 临时变量 in 要遍历的集合)
{
//将临时变量作为遍历的数据使用
}
要知道临时数据类型是什么类型的可以先写个var 然后在下面设个断点,再把光标放到var上去,就可以得到当前遍历的集合是什么类型了
尽量不要用var
四、装箱与拆箱
值类型转换为引用类型就是装箱
应用类型转换为值类型就是拆箱
对值类型装箱保持不变性,对引用类型不保持保持相关性()
Dotnet学习_递归篇
三、递归
递归就是调用自己
--》怎么调用?(递推关系)
--》什么时候跳出
等差数列
2 4 6 8 10 ...
求第n项
2n
Func(n) = Func(n-1) + 2
Func(n) = (Func(n-1-1)+2)+2
。。。
Func(n) = (Func(1)。。。)。。。+2
写递归
--》首先要找到递推关系
--》临界条件
小练习_神探福尔摩斯小说
private void Form1_Load(object sender, EventArgs e) { //先添加一个根节点 TreeNode tn = tvwDir.Nodes.Add("神探福尔摩斯-柯南.道尔"); //获得文件的路径 string path= Path.GetFullPath ("txt"); RecAdd(tn, path); } private void RecAdd(TreeNode tn, string path) { //获得这个路径下的所有子目录 string [] dirs = Directory.GetDirectories(path); //获得这个路径下的所有子文件 string[] files = Directory.GetFiles(path); //循环把所有的子目录添加到父节点下 foreach (string item in dirs) { //从绝对路径里提取文件名 string file= Path.GetFileNameWithoutExtension(item); TreeNode tn1= tn.Nodes.Add(file);//目录名添加到父节点下 //递归调用,因为不知道子目录下还有多少个子目录,所以递归调用 RecAdd(tn1, item); } //循环添加所有的子文件到父节点下 foreach (string item in files) { //同样的需要对绝对路径做一下处理,提取文件名 string file = Path.GetFileNameWithoutExtension(item); TreeNode tn2= tn.Nodes.Add(file);//文件名添加到父节点下 tn2.Tag = item; //tag属性石用来存储程序员需要使用的任何数据 //把绝对路径保存到刚添加的节点的tag属性下,用于后面读取文件里的内容 } } private void tvwDir_AfterSelect(object sender, TreeViewEventArgs e) { TreeNode tn= e.Node ;//先获取选中的节点 if (tn.Tag !=null)//判断tag属性是不是空 { txtContans.Text = File.ReadAllText(tn.Tag .ToString (),Encoding .Default ); } }
C# 深拷和浅拷篇
二、深拷与浅拷
拷就是拷贝、复制的意思
--》深拷和浅拷是指堆空间里的对象和引用类型的拷贝,而值类型不存在深拷和浅拷,因为值类型只是把值给复制了一份。
浅拷
--》浅拷,只要有一个引用类型没有被复制就是浅考,(复制就是产生了一个完全没有关系的对象,而这个对象里德值是被复制过来的)
class Mark { string _name; public string Name { get { return _name; } set { _name = value; } } } class MyClass { string _name; Mark mark; public Mark Marks { get { return mark; } set { mark = value; } } public string Name { get { return _name; } set { _name = value; } } public MyClass Coby() { MyClass temp=new MyClass (); temp.Name = this.Name; temp.mark = new Mark(); temp.mark.Name = this.mark.Name; return temp; } } class Program { static void Main(string[] args) { MyClass m1 = new MyClass(); m1.Name = "张三"; m1.Marks = new Mark(); m1.Marks.Name = "哈哈哈"; MyClass m = m1; //这里只是把m1的引用复制了一份赋给了m 并没有复制m1里的对象也没有复制引用类型所以既不是深考也不是浅考 MyClass m2 = new MyClass(); //浅考 m2.Name = m1.Name; m2.Marks = m1.Marks;//这里是把m1Marks里的对象复制了,m1.marks存的引用又复制了一份赋给m2.marks此时他们都是指向了堆空间里的另一个对象mark,引用类型没有被完全复制在mark这里断开了,所以是浅考 MyClass m3 = new MyClass(); m3.Name = m1.Name; m3.Marks = new Mark(); m3.Marks.Name = m1.Marks.Name;//这里m3是重新New一个对象,然后把m1.marks.name的值复制给m3.marks.name,他们是两个完全不一样的对象,此时m1里的引用类型被m3完全的复制了一份,所以叫深考, //只要 所有的引用类型都被复制了没有一个被断开就是深考 }
C# 序列化篇
一、序列化
为什么要序列化? --》为了把内存里的数据写到硬盘里。
序列化首先 第一步:给类加一个标签,标记为可以被序列化,要序列化的对象类与父类均需标记
[Serializable] //标记 (特性的意思) Class myClass { } static void Main(string[] args) { #region 传统方法 //Student stu = new Student(); //stu.Name = "张三"; //stu.Sex='男'; //stu.Age = 20; ////将student对象存到 //StringBuilder sb = new StringBuilder(); //sb.AppendLine(stu.Name ); //sb.AppendLine(stu.Age.ToString () ); //sb.AppendLine(stu.Sex .ToString ()); //File.WriteAllText("E:\\sb.txt", sb.ToString()); //string []input= File.ReadAllLines("E:\\sb.txt"); //Student s=new Student (); //s.Name = input[0]; //s.Age =Convert .ToInt32 ( input[1]); //s.Sex = Convert.ToChar(input[2]); #endregion List<Student> list = new List<Student>(); list.Add(new Student ("张三1",20,'男')); list.Add(new Student("张三2", 20, '男')); list.Add(new Student("张三3", 20, '男')); list.Add(new Student("张三4", 20, '男')); list.Add(new Student("张三5", 20, '男')); list.Add(new Student("张三6", 20, '男')); list.Add(new Student("张三7", 20, '男')); list.Add(new Student("张三8", 20, '男')); list.Add(new Student("张三9", 20, '男')); #region 荣誉代码 //using (FileStream file = new FileStream("E:\\1.dat", FileMode.Create, FileAccess.Write)) //{ // BinaryFormatter bin = new BinaryFormatter(); // bin.Serialize(file, list); //} //Console.WriteLine("序列化完成"); //using (FileStream wfile = new FileStream("E:\\1.dat", FileMode.Open, FileAccess.Read)) //{ // BinaryFormatter bin=new BinaryFormatter (); // Student s = (Student)bin.Deserialize(wfile); //} #endregion //序列化 //首先需要一个写得流 using (FileStream filewrite = new FileStream("E:\\1.dat", FileMode.Create, FileAccess.Write)) { //再需要一个工具 BinaryFormatter bin = new BinaryFormatter(); bin.Serialize(filewrite, list); //第一个是流,第二个是对象 } //反序列化 //首先也需要一个读的流 using (FileStream fileread = new FileStream("E:\\1.dat", FileMode.Open, FileAccess.Read)) { //再需要一个工具 BinaryFormatter fbin = new BinaryFormatter(); List <Student > s = (List <Student >)fbin.Deserialize(fileread); } Console.WriteLine("完成"); }
序列化的一个应用---保存窗体上一次关闭时的位置
--》首先写一个方法,保存窗体的位置(窗体的左上角的坐标)和窗体的长和宽,在关闭窗体的时候调用这个方法。(写在Dispose方法里 关闭窗体时都会调用这个方法)
protected override void Dispose(bool disposing) { //调用保存窗体关闭时的位置大小 Save(); if (disposing && (components != null)) { components.Dispose(); } base.Dispose(disposing); --》然后要一个类,这个类里面有两个字段 来存储窗体的location和size属性,把这儿两个属性分别赋值给这个类的两个字段 class MyPoint { Point mPoint; Size mySize; public Point MPoint { get; set; } public Size MySize { get; set; } }
--》然后序列化把数据写到硬盘里保存,当窗体关闭的时候调用这个方法就保存了窗体关闭时的位置和大小。
--》再写一个方法反序列化,把存到硬盘里德数据再拿出来,写窗体的 Lode事件(窗体启动前都会执行lode事件,lode事件一般都用来初始化),
把定义类的两个字段里存的数据再赋给窗体。
private void Save() {//需要一个流 using (FileStream filewrite = new FileStream("location.dat", FileMode.Create, FileAccess.Write)) { //new一个MyPoint类用来存储窗体的位置大小 MyPoint mypoint = new MyPoint(); mypoint.MPoint = this.Location;//把窗体的位置赋给字段(窗体的左上角的point) mypoint.MySize = this.Size;//把窗体的大小赋给字段 //需要一个序列化的工具 BinaryFormatter bin = new BinaryFormatter(); //开始序列化 bin.Serialize(filewrite, mypoint); } } private void ReadLocatoin() { if (File.Exists("location.dat"))//判断一下窗体的位置大小被改变了没有,如果改变了会产生一个"location.dat"文件 { //需要一个流 using (FileStream fileread = new FileStream("location.dat", FileMode.Open, FileAccess.Read)) { //需要一个反序列化的工具 BinaryFormatter bin = new BinaryFormatter(); //开始反序列化 MyPoint p = (MyPoint)bin.Deserialize(fileread); //把窗体的启动起始位置设置为自定义 this.StartPosition = FormStartPosition.Manual; this.Location = p.MPoint; this.Size = p.MySize; } } } private void Form1_Load(object sender, EventArgs e) { ReadLocatoin(); }
//最近工作事情多,本系列下章应该会延时。
当你无法控制自己的情绪
将时间一分一秒地花在随大流、追热点、逞能斗气、不干实事
人生就会像一架坏掉的机器,创造不出优质的产品