C#基础学习笔记(十一)
0、复习
class Program
{
static void Main(string[] args)
{
#region 里氏转换
//里氏转换
//1、子类可以赋值给父类(如果有一个方法需要一个父类作为参数,我们可以传第一个子类对象)
//2、如果父类装的是子类对象则可以将这个父类转换成子类对象
//Person p = new Person();
//p.PersonSayHello();
//把子类赋给父类
Person p = new Student();
//is as 转换
//is 转换成功返回true 转换失败返回false
if (p is Student)
{
((Student)p).StudentSayHello();
}
else
{
Console.WriteLine("转换失败");
}
//as 转换成功返回转换对象 失败返回null
Student ss = p as Student;
ss.StudentSayHello();
#endregion
#region ArrayList
ArrayList list = new ArrayList();
//Remove RemoveAt RemoveRange Clear Insert InsertRange Reverse Sort
Hashtable ht = new Hashtable();
ht.Add(1, "张三");
ht.Add(true, '男');
ht.Add(3.14, 500m);
//在键值对集合中,键必须是唯一的
//ht.Add(1, "李四");
//ht.ContainsKey
ht[1] = "李四";
foreach (var item in ht.Keys)
{
Console.WriteLine("键{0}-->值{1}", item, ht[item]);
}
#endregion
//Path
#region File
//File
//Create Delete Copy Move
byte[] buffer = File.ReadAllBytes(@"C:\Users\11466\Desktop\copy.txt");
//将字节数组中的每一个元素都要按照我们指定的编码格式解码成字符串
//UTF-8 GB2312 GBK ASCII Unicde
string s = Encoding.GetEncoding("GBK").GetString(buffer);
Console.WriteLine(s);
//当在读取类似音乐等其他文件时用WriteAllBytes,文本文件可以用下面两个
//没有这个文件的话会创建一个 有的话会覆盖
string str = "今天天气真的好";
//需要将字符串转换成字节数组
byte[] butter = Encoding.Default.GetBytes(str);
File.WriteAllBytes(@"C:\Users\11466\Desktop\new.txt", butter);
Console.WriteLine("写入成功");
//这两方法的区别就是一个返回的是数组 一个返回的是string
//如果要继续操作字符串就用ReadAllLines
//按照每行读取
string[] content = File.ReadAllLines(@"C:\Users\11466\Desktop\copy.txt", Encoding.UTF8);
foreach (string item in content)
{
Console.WriteLine(item);
}
//按照文件读取
//绝对路径
string str = File.ReadAllText(@"C:\Users\11466\Desktop\copy.txt", Encoding.UTF8);
Console.WriteLine(str);
//这就是相对路径(要放在同一的文件夹下debug下)
string str = File.ReadAllText("copy.txt", Encoding.UTF8);
Console.WriteLine(str);
//写入
//写一行
File.WriteAllLines(@"C:\Users\11466\Desktop\new.txt", new string[] { "aoe", "efwsef" });
Console.WriteLine("OK");
File.WriteAllText(@"C:\Users\11466\Desktop\new.txt", "我复活节哦就发皮哦就 佛脚");
Console.WriteLine("OK");
File.AppendAllText(@"C:\Users\11466\Desktop\new.txt", "我是后面的");
Console.WriteLine("OK");
#endregion
Console.ReadKey();
}
}
public class Person
{
public void PersonSayHello()
{
Console.WriteLine("我是人类");
}
}
//子类可以调用子类自己的方法还可以调用父类的方法
public class Student : Person
{
public void StudentSayHello()
{
Console.WriteLine("我是学生");
}
}
1、绝对路径和相对路径
-
绝对路径:通过给定的这个路径直接能在我的电脑中找到这个文件。
-
相对路径:文件相对于应用程序的路径。
-
结论:我们在开发中应该去尽量的使用相对路径。
2、泛型集合
List<T>
泛型集合
就是为了专门处理某种类型
ArrayList对应的是 List<类型名>
在尖括号中写什么类型,这个集合就变成了什么类型的集合
添加数据、插入数据、索引访问数据都是这个类型的,不用考虑所有的转化问题
static void Main(string[] args)
{
//创建泛型集合对象
List<int> list = new List<int>();
list.Add(1);
list.Add(2);
list.Add(3);
list.AddRange(new int[] { 1, 2, 3, 4, 5, 6, });
list.AddRange(list);
//List泛型集合可以转换成数组
//转成什么样的数组取决于你是什么样的集合
int[] nums = list.ToArray();
List<string> listStr = new List<string>();
string[] str = listStr.ToArray();
数组转成集合
//List<int> listTwo = nums.ToList();
char[] chs = new char[] { 'a', 'b', 'c' };
List<char> listChar = chs.ToList();
for (int i = 0; i < listChar.Count; i++)
{
Console.WriteLine(listChar[i]);
}
for (int i = 0; i < list.Count; i++)
{
Console.WriteLine(list[i]);
}
Console.ReadKey();
}
3、装箱、拆箱
装箱:就是将值类型转换为引用类型。
拆箱:将引用类型转换为值类型。
- 看两种类型是否发生了装箱或者拆箱,要看,这两种类型是否存在继承关系。
static void Main(string[] args)
{
//int n = 10;
装箱 将值类型转换为引用类型
//object o = n;
拆箱 将引用类型转换为值类型
//int nn = (int)o;
ArrayList list = new ArrayList();
List<int> list1 = new List<int>();
Stopwatch sw = new Stopwatch();
sw.Start();
//for (int i = 0; i < 10000000; i++)
//{
// //ArrayList添加数据的时候是用object
// //而i是值类型,所以这是装箱的过程
// //这里需要1000000次的装箱操作
// //00:00:01.7941273
// list.Add(i);
//}
for (int i = 0; i < list1.Count; i++)
{
//此时都为int类型 不需要装箱或者封箱
//时间短
list.Add(i);
}
sw.Stop();
Console.WriteLine(sw.Elapsed);
这个地方没有发生任意类型的装箱或者拆箱
没有什么交集
//string str = "123";
//int n = Convert.ToInt32(str);
//看两种类型是否发生了装箱或者拆箱,要看,这两种类型是否存在继承关系。
int n = 30;
//装箱,继承关系
IComparable i = n;
Console.ReadKey();
}
4、Dictionary
Dictionary <TKey,TValue>
默认提供命名空间,提倡使用
Hashtable对应的是 Dictionary<键的类型, 值的类型>
在尖括号中填入键的类型与值的类型,那么这个集合就变成了一个指定的键值对模型其使用方式与Hashtable一样
举例:
static void Main(string[] args)
{
//HashTable类似
Dictionary<int, string> dic = new Dictionary<int, string>();
dic.Add(1, "张三");
dic.Add(2, "李四");
dic.Add(3, "王五");
键值不重复
//dic.Add(1, "abc");
dic[1] = "新来的";
//foreach (var item in dic.Keys)
//{
// Console.WriteLine("{0}-->{1}", item, dic[item]);
//}
//新的遍历方法
foreach (KeyValuePair<int,string> kv in dic)
{
Console.WriteLine("{0}-->{1}", kv.Key, kv.Value);
}
Console.ReadKey();
}
泛型集合的练习:
static void Main(string[] args)
{
#region 题一
//将一个数组中的奇数放到一个集合中,再将偶数放到另一个集合中
//最终将两个集合合并为一个集合,并且奇数显示在左边,偶数在右边
int[] nums = { 1, 2, 3, 4, 5, 6, 7, 8, 9 };
List<int> listOu = new List<int>();
List<int> listJi = new List<int>();
for (int i = 0; i < nums.Length; i++)
{
if (nums[i] % 2 == 0)
{
listOu.Add(nums[i]);
}
else
{
listJi.Add(nums[i]);
}
}
//奇数显示在左边,偶数在右边
//谁在左边就谁添加谁
listJi.AddRange(listOu);
foreach (int item in listJi)
{
Console.Write(item + " ");
}
Console.ReadKey();
可以在原来的集合中操作
//List<int> listnum = new List<int>();
//listnum.AddRange(listOu);
//listJi.AddRange(listJi);
#endregion
#region 题二
//让用户输入一个字符串,通过foreach循环将用户输入的字符串赋值给一个字符数组
Console.WriteLine("请输入一个字符串");
string input = Console.ReadLine();
char[] chs = new char[input.Length];
int i = 0;
foreach (var item in input)
{
chs[i] = item;
i++;
}
foreach (var item in chs)
{
Console.WriteLine(item);
}
Console.ReadKey();
#endregion
#region 题三
//统计Welcome to China中每个字符串出现的次数 不考虑大小写
//拿到一个字符就要拿到它出现的次数
//字符---->出现的次数
//键---->值
string str = "Welcome to China";
Dictionary<char, int> dic = new Dictionary<char, int>();
for (int i = 0; i < str.Length; i++)
{
if (str[i] == ' ')
{
continue;
}
//如果dic已经包含了当前循环到的这个键
if (dic.ContainsKey(str[i]))
{
//值加一
dic[str[i]]++;
}
else
{
//否则就是第一次出现
dic[str[i]] = 1;
}
}
foreach (KeyValuePair<char, int> kv in dic)
{
Console.WriteLine("字母{0}出现了{1}", kv.Key, kv.Value);
}
Console.ReadKey();
#endregion
}
- 将创建文件流对象的过程写在using当中,会自动的帮助我们释放流所占用的资源。
文件流:
static void Main(string[] args)
{
#region 使用FileStream来读取数据
//FileStream 操作字符的
//StreamReader和StreamWriter 操作字符的
//路径 对文件的操作 对数据的操作
FileStream fsRead = new FileStream(@"C:\Users\11466\Desktop\new.txt", FileMode.OpenOrCreate, FileAccess.Read);
//限定读的大小只有5M
byte[] buffer = new byte[1024 * 1024 * 5];
//返回本次实际读取到的有效字节数
int r = fsRead.Read(buffer, 0, buffer.Length);
//将字节数组中每一个元素按照指定的编码格式解码格式解码成字符串
//有效的很小,超过的部分都用空来填 不好
string s = Encoding.UTF8.GetString(buffer, 0, r);
//关闭流
fsRead.Close();
//释放流所占用的资源
fsRead.Dispose();
Console.WriteLine(s);
#endregion
#region 使用FileStream来写入数据
//使用FileStream来写入数据
//将创建文件流对象的过程写在using当中,会自动的帮助我们释放流所占用的资源
using (FileStream fsWrite = new FileStream(@"C:\Users\11466\Desktop\new.txt", FileMode.OpenOrCreate, FileAccess.Write))
{
//用字节数组来写入的
//不是完全的覆盖
string str = "看我有没有把我给你覆盖掉";
byte[] buffer = Encoding.UTF8.GetBytes(str);
fsWrite.Write(buffer, 0, buffer.Length);
}
Console.WriteLine("写入成功");
#endregion
Console.ReadKey();
}
- 使用文件流来实现多媒体文件的复制:
static void Main(string[] args)
{
//思路:首先将要复制的多媒体文件读取出来,然后在写入到你指定的位置
string source = @"C:\Users\11466\Desktop\视频.mp4";
string target = @"C:\Users\11466\Desktop\new.mp4";
CopyFile(source, target);
Console.WriteLine("复制成功");
Console.ReadKey();
}
public static void CopyFile(string soucre, string target)
{
//1、我们创建一个负责读取的流
using (FileStream fsRead = new FileStream(soucre, FileMode.Open, FileAccess.Read))
{
//2、创建一个负责写入的流
using (FileStream fsWrite = new FileStream(target, FileMode.OpenOrCreate, FileAccess.Write))
{
byte[] buffer = new byte[1024 * 1024 * 5];
//可能文件可能会比较大,所以我们在读取的时候,应该通过一个循环去读取
while (true)
{
//返回本次读取实际读取到的字节数
int r = fsRead.Read(buffer, 0, buffer.Length);
if (r == 0)
{
break;
}
fsWrite.Write(buffer, 0, r);
}
}
}
}
5、StreamReader和StreamWriter的用法
static void Main(string[] args)
{
//FileStream是操作字节的 可以操作所有的文件
//StreamReader是操作字符的 文本的
//使用StreamWriter来读取一个文本文件
using (StreamReader sr = new StreamReader(@"C:\Users\11466\Desktop\new.txt", Encoding.UTF8))
{
//Console.WriteLine(sr.ReadLine());
//是否读完了
while (!sr.EndOfStream)
{
Console.WriteLine(sr.ReadLine());
}
}
//使用StreamWriter来写入一个文本文件
using (StreamWriter sw = new StreamWriter(@"C:\Users\11466\Desktop\newnew.txt", true))
{
//sw.Write("客户方打不开ID被罚款绝大部分快递费");
//上面改为true就可以继续添加了
sw.Write("看我有没有给你覆盖掉");
}
Console.WriteLine("写入OK");
Console.ReadKey();
}
6、实现多态的手段
1)、虚方法
步骤:
1、将父类的方法标记为虚方法 ,使用关键字 virtual,这个函数可以被子类重新写一个遍。
class Program
{
static void Main(string[] args)
{
//步骤:
//要在题目给定的类里边确定一个父类
//父类virtual,子类override
#region 题一
//真的鸭子嘎嘎叫,木头鸭子吱吱叫,橡皮鸭子唧唧叫
RealDuck rd = new RealDuck();
//rd.Bark();
MuDuck md = new MuDuck();
//md.Bark();
XpDuck xd = new XpDuck();
//xd.Bark();
//多态做法
RealDuck[] ducks = { rd, md, xd };
for (int i = 0; i < ducks.Length; i++)
{
ducks[i].Bark();
}
#endregion
//经理十一点打卡 员工十点打卡 实习生九点打卡
#region 题二
Employee emp = new Employee();
Manager mg = new Manager();
Interns it = new Interns();
Employee[] emps = { emp, mg, it };
for (int i = 0; i < emps.Length; i++)
{
emps[i].Daka();
}
#endregion
Console.ReadKey();
}
}
//传统做法
public class RealDuck
{
public void Bark()
{
Console.WriteLine("真的鸭子嘎嘎叫");
}
}
public class MuDuck
{
public void Bark()
{
Console.WriteLine("木头鸭子吱吱叫");
}
}
public class XpDuck
{
public void Bark()
{
Console.WriteLine("橡皮鸭子唧唧叫");
}
}
//多态做法
#region 多态方法一
public class RealDuck
{
public virtual void Bark()
{
Console.WriteLine("真的鸭子嘎嘎叫");
}
}
public class MuDuck : RealDuck
{
public override void Bark()
{
Console.WriteLine("木头鸭子吱吱叫");
}
}
public class XpDuck : RealDuck
{
public override void Bark()
{
Console.WriteLine("橡皮鸭子唧唧叫");
}
}
#endregion
#region 多态方法二
public class Employee
{
public virtual void Daka()
{
Console.WriteLine("十点打卡");
}
}
public class Manager : Employee
{
public override void Daka()
{
Console.WriteLine("经理十一点打卡");
}
}
public class Interns : Employee
{
public override void Daka()
{
Console.WriteLine("实习生九点打卡");
}
}
#endregion
}
2)、抽象类
当父类中的方法不知道如何去实现的时候,可以考虑将父类写成抽象类,将方法写成抽象方法。
class Program
{
static void Main(string[] args)
{
//当父类中的方法不知道如何去实现的时候,可以考虑将父类写成抽象类,将方法写成抽象方法 abstract
//狗狗会叫 猫猫会叫
//抽象类不允许创建实例
Animal a = new Dog();
a.Bark();
Console.ReadKey();
}
}
public abstract class Animal
{
//也不知道怎么实现,没有方法体
//让子类重写,达成多态
public abstract void Bark();
//子类继承抽象类后,必须把父类中的所有抽象成员都重写
public abstract string Name
{
//继承的类没有实现它的属性会报错
get;
set;
}
//如果是非抽象成员呢?
#region 非抽象的
//一下的都是非抽象的
//可以写,父类不能用,但是继承的子类可以
private int _age;
public int Age
{
get { return _age; }
set { _age = value; }
}
public Animal(int age)
{
this.Age = age;
}
//抽象类也有构造函数,但是无法实例化就无法进去
public Animal()
{
}
#endregion
//抽象类中的抽象方法
//既有返回值和参数(签名)
//在子类实现的时候也要有返回值和参数
public abstract string TestString(string name);
注意区别
//public void Test()
//{
// //空实现
//}
//抽象类当中的虚方法
public virtual void TestVirtual()
{
Console.WriteLine("都有生命");
}
//不需要提供抽象的成员就没有必要写抽象类
}
//这就不用重写了
public abstract class Test : Animal
{
}
public class Dog : Animal
{
//必须在抽象类中
//public abstract void Test();
public override string Name
{
get
{
throw new NotImplementedException();
}
set
{
throw new NotImplementedException();
}
}
public override void Bark()
{
Console.WriteLine("狗狗汪汪叫");
}
//子类要重写相同的方法,签名相同
public override string TestString(string name)
{
throw new NotImplementedException();
}
}
public class Cat : Animal
{
public override string Name
{
get
{
throw new NotImplementedException();
}
set
{
throw new NotImplementedException();
}
}
public override void Bark()
{
Console.WriteLine("猫咪喵喵的叫");
}
public override string TestString(string name)
{
throw new NotImplementedException();
}
}
抽象类的特点:
1、抽象成员必须标记为abstract,并且不能有任何实现。
2、抽象成员必须在抽象类中。
3、抽象类不能被实例化
4、子类继承抽象类后,必须把父类中的所有抽象成员都重写。(除非子类也是一个抽象类,则可以不重写)
5、抽象成员的访问修饰符不能是private
6、在抽象类中可以包含实例成员,并且抽象类的实例成员可以不被子类实现
7、抽象类是有构造函数的。虽然不能被实例化。
8、如果父类的抽象方法中有参数,那么。继承这个抽象父类的子类在重写父类的方法的时候必须传入对应的参数。
9、果抽象父类的抽象方法中有返回值,那么子类在重写这个抽象方法的时候 也必须要传入返回值。
10、如果父类中的方法有默认的实现,并且父类需要被实例化,这时可以考虑将父类定义成一个普通类,用虚方法来实现多态。
11、如果父类中的方法没有默认实现,父类也不需要被实例化,则可以将该类定义为抽象类。
练习:
class Program
{
static void Main(string[] args)
{
//用多态来实现 将移动硬盘或者U盘或者MP3插到电脑上进行读写数据
//MobileDisk md = new MobileDisk();
//UDisk u = new UDisk();
//Mp3 mp3 = new Mp3();
//Computer cpu = new Computer();
U盘插入
//cpu.CupRead(u);
//cpu.CpuWrite(u);
多态
//MobileStorage ms = new UDisk();
//Computer cpu = new Computer();
//cpu.CpuRead(ms);
//cpu.CpuWrite(ms);
//如果使用字段来调用父类,则
MobileStorage ms = new Mp3();
Computer cpu = new Computer();
cpu.Ms = ms;
cpu.CpuRead();
cpu.CpuWrite();
Console.ReadKey();
}
}
/// <summary>
/// 抽象的父类
/// </summary>
public abstract class MobileStorage
{
public abstract void Read();
public abstract void Write();
}
public class MobileDisk : MobileStorage
{
public override void Read()
{
Console.WriteLine("移动硬盘在读取数据");
}
public override void Write()
{
Console.WriteLine("移动硬盘在写入数据");
}
}
public class UDisk : MobileStorage
{
public override void Read()
{
Console.WriteLine("U盘在读取数据");
}
public override void Write()
{
Console.WriteLine("U盘在写入数据");
}
}
public class Mp3 : MobileStorage
{
public override void Read()
{
Console.WriteLine("MP3在读取数据");
}
public override void Write()
{
Console.WriteLine("MP3在写入数据");
}
public void PlayMusic()
{
Console.WriteLine("MP3自己可以播放音乐");
}
}
public class Computer
{
//拿到父类的方法,第一种就是传参的方法
//第二种可以用字段去存
private MobileStorage _ms;
public MobileStorage Ms
{
get { return _ms; }
set { _ms = value; }
}
public void CpuRead()
{
Ms.Read();
}
public void CpuWrite()
{
Ms.Write();
}
传参的虽然是父类,但是
//public void CpuRead(MobileStorage ms)
//{
// ms.Read();
//}
//public void CpuWrite(MobileStorage ms)
//{
// ms.Write();
//}
}