02 C#高级

第九天

 

面向过程--à面向对象

面向过程:面向的是完成这件事儿的过程,强调的是完成这件事儿的动作。

 

把大象塞进冰箱

1、 打开冰箱门

2、 把大象塞进去,亲下大象的屁股

3、 关闭冰箱门

 

孙全 瘦小 矮 小屌丝

孙全踩着小板凳打开冰箱门

孙全找翟盼盼帮忙把大象塞进冰箱里,孙全踩着板凳去亲。

孙全踩着板凳关闭冰箱门

 

翟盼盼 190cm 非常大力气

1、 翟自己就能打开冰箱门

2、 翟自己将大象塞进冰箱里,翟可以自己亲一下。

3、 翟自己关闭冰箱门

 

如果我们用面向过程的思想来解决这件事儿,当执行这件事的人不同的时候,我们需要为每个不同的人量身定做解决问题的方法。

 

面向对象:找个对象帮你做事儿。

把大象塞进冰箱里

我们把冰箱作为对象:

1、 冰箱门可以被打开

2、 大象可以被塞进冰箱里

3、 冰箱门可以被关闭

 

孙全

孙全  1

孙全  2

孙全  3

 

翟盼盼

翟  1

翟  2

翟  3

 

面向对象:意在写出一个通用的代码,屏蔽差异

 

关门

面向过程:关门

张三:一脚把门揣紧了

李四:轻轻地把门关上了

王五:门没关严,留了个尾巴

面向对象:关门

门可以被关闭

 

我在黑板上画了一个方块

将 圆圈作为对象

圆圈可以被画在黑板上

 

将 黑板作为对象

黑板可以被画圆

黑板可以被画方块

 

 

语法

[public] class 类名

{

  字段:

  属性:

方法:

}

写好了一个类之后,我们需要创建这个类的对象,那么,我们管创建这个类的对象过程称之为类的实例化。

使用关键字 new.

 

this表示当前类的对象

类是不占内存的,而对象是占内存的。

 

 

结构//面向过程

类//面向对象

 

结构不具备面向对象的任何特征。

*面向对象的特征:封装、继承、多态。

 

属性

属性的作用就是保护字段、对字段的赋值和取值进行限定。

属性的本质就是两个方法,一个叫get一个叫set

 

Field字段

Method方法

Property属性

 

****字段就是女人 属性才是男人****

女人就是在家里面呆着,相夫教子

所有跟外界打交道的事情让男人去做

 

4、 访问修饰符

public:公开的,公共的,在哪都能访问

private:私有的,只能在当前类的内部进行访问,出了这个类就访问不到了

不加的话默认就是private

 

字段必须是私有的

既有get也有set我们称之为可读可写属性

只有get 我们称之为只读属性

只有set 我们称之为只写属性

 

5、

当我们创建号一个类的对象后,需要给这个对象的每个属性去赋值

我们管这个过程称之为对象的初始化

 

6、

静态和非静态的区别

1)     在非静态类中,即可以有实例成员,也可以有静态成员。

2)     在调用实例成员的时候,需要使用对象名.实例成员。

3)     在调用静态成员的时候,需要使用类名.静态成员名。

 

 

总结:

1静态成员必须使用类名去调用,而实例成员使用对象名调用。

 

2.静态函数中只能访问静态成员,不允许访问实例成员。

而实例函数中,既可以使用静态成员,也可以使用实例成员。

 

静态类中只允许有静态成员,不允许出现实例成员,

 

使用:

1)、如果你想要你的类当作一个“工具类”去使用,这个时候可以考虑将类写成静态类

2)、静态类在整个项目中资源共享。

3)、只有在程序全部结束后,程序才会释放资源。

 

堆 栈 静态存储区域

 

释放资源。 GC Garbage Collection垃圾回收器

 

再总结一下:

非静态在实例化中就能调用。

静态在类名中就能调用。

 

7、构造函数

作用:帮助我们初始化对象(给对象的每个属性依次赋值)

构造函数是一个特殊的方法:

1)、构造函数没有返回值,连void也不能写。

2)、构造函数的名称必须跟类名一样。

 

创建对象的时候会执行构造函数。

构造函数是可以有重载的。

 

类当中会有一个默认的无参数的构造函数,当你写了一个新的构造函数后,不管是有参数的还是无参数的,那个默认的无参数的构造函数都被干掉了。

 

8、new关键字

Person zsPerson = new Person();

new帮助我们做了3件事儿:

1)、在内存中开辟一块空间

2)、在开辟的空间中创建对象

3)、调用对象的构造函数进行初始化对象

(所以构造函数必须得是public)

9、this关键字

1)、代表当前类的对象

2)、在类当中显示的调用本类的构造函数 :this

(注意这里是 冒号+this  :this)

讲课的时候用了这样的一个例子:

 

 

在Student类中:

        public Student()

        {

        }

 

        public Student(string name, int age, char gender, int chinese, int math, int english)

        {

            this.Name = name;

            this.Age = age;

            this.Gender = gender;

            this.Chinese = chinese;

            this.Math = math;

            this.English = english;

        }

 

        public Student(string name, int chinese, int math, int english):this(name,0,'c',chinese,math,english)

        {

            //this.Name = name;

            //this.Chinese = chinese;

            //this.Math = math;

            //this.English = english;

            Console.WriteLine("Done!");

        }

 

在Main函数中:

  Student s = new Student("张三",100,100,100);

 

执行顺序是先走到4个参数的构造函数的头部(不进去),然后进入到6个参数的构造函数中执行,执行完后执行4个参数的构造函数。

 

 

 

 

 

 

第十天

 

命名空间

可以认为类是属于命名空间的。

如果在当前项目中没有这个类的命名空间,需要我们手动的导入这个类所在的命名空间。

1)、用鼠标去点

2)、alt+shift+F10

3)、记住命名空间,手动的去引用

 

2、在一个项目中引用另一个项目的类

1)、添加引用

2)、引用命名空间

 

3、值类型和引用类型

区别:

1、 值类型和引用类型在内存上存储的地方不一样。

2、 在传递值类型和传递引用类型的时候,传递的方式不一样。

值类型我们称之为值传递,引用类型我们称之为引用传递。

 

我们学的值类型和引用类型:

值类型: int、double、bool、char、decimal、struct、enum

引用类型:string、数组、自定义类、、接口

 

存储:

值类型的值是存储在内存的栈当中,

引用类型的值是存储在内存的堆当中,

s在栈上存的就是123在堆中的地址

值类型存在栈中,引用类型存在堆中,

但是栈中也有存了堆空间中的地址

 

1、 字符串

1)、字符串的不可变性

当你给一个字符串重新赋值之后,老值并没有销毁,而是重新开辟一块空间存储新值

当程序结束后,GC扫描整个内存,如果发现有的空间没有被指向,则立即把它销毁。

 

2)、我们可以将字符串看作是char类型的一个只读数组。

 

string a = “asdasd”;

a[0] = ‘b‘ //这样不行,因为string是只读的,虽然能读出a[0],但是并不能给a[0]赋值。

 

那么如何处理呢?

//首先将字符串转换为char类型的数组

 

char[] chs = a.ToCharArray();

 

chs[0] = ‘b’;

 

s = new string(chs);

//内存在堆中又开了个新空间

 

ToCharArray();将字符串转换为char数组

new string(char[] chs);能够将char数组转换为字符串

 

 

*Stopwatch类 计时器

Stopwatch sw = new Stopwatch();

//创建了一个计时器,用来记录程序运行的时间

sw.Start();//开始计时

。。。。。。

sw.Stop();//结束计时

Console.WriteLine(sw.Elapsed);

 

Elapse——消逝,过去

 

1、 字符串提供的各种方法

1)、Length:获得当前字符串中字符的个数

2)、ToUpper():将字符转换成大些形式

3)、ToLower():将字符串转换成小谢形式

4)、Equals(lessonTwo,StringComparision.OrdinalIgnoreCase):比较两个字符串,忽略大小写。

5)、Split():分割字符串,返回字符串类型的数组

6)、Substring():截取字符串,在截取的时候包含要截取的那个位置。

7)、IndexOf():判断某个字符串在字符串中第一次出现的位置,如果没有返回-1

8)、LastIndexOf():判断某个字符串在字符串中最后一次出现的位置,如果没有返回-1

9)、StartsWith():判断以…开始

10)、EndsWith():判断以…结束

11)、Replace():将字符串中某个字符串替换成一个新的字符串

12)、Contains():判断某个字符串是否包含指定的字符串

13)、Trim():去掉字符串中前后的空格

14)、TrimEnd():去掉字符串中结尾的空格

15)、TrimStart()去掉字符串中前面的空格

16)、string.IsNullOrEmpty():判断一个字符串是否为空或者为null

17)、Array.Join():将数组按照制定的字符串连接,返回一个字符串

 

 

 

 复习:

 

1、 继承

我们可能会在一些类中,写一些重复的成员,我们可以将这些重复的成员,单独的封装到一个类中,作为这些类的父类。

Student、Teacher、Driver 子类

Person                  父类

 

子类继承了父类,那么子类从父类那里继承过来了什么?

首先,子类继承了父类的属性和方法,但是子类并没有继承父类的私有字段

 

问题:子类有没有继承父类的构造函数?

答:子类并没有继承父类的构造函数,但是。子类会默认的调用父类无参数的构造函数。

创建父类对象,让子类可以使用父类中的成员。

所以,如果在父类中重新写了一个有参的构造函数之后,哪个无参数的就被干掉了,

子类就调用不到了,所以子类会报错。

解决办法:

1)、在父类中重新写一个无参数的构造函数。

2)、在子类中显示的调用父类的构造函数,使用关键字:base()

 

 

1、 继承的特性

1、 继承的单根性:一个子类只能有一个父类。

2、 继承的传递性:

(单根性:一个孩子只能有一个爹)

(传递性:祖宗的迟早都是我的)

 

7、查看类图

 

8、Object是所有类的基类

 

9、new关键字

1)、创建对象

2)、隐藏从父类那里继承过来的同名成员。

隐藏的后果就是子类调用不到父类的成员,并且也不会报警告。

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

第十一天:

 

1、 里氏转换

1)、子类可以赋值给父类。

 

例如:

Person p = new Student();

 

2)、如果父类中装的是子类对象,那么可以将这个父类对象强转为子类对象。

 

例如:

Student s = (Student)p;

s.StudentSayHello();

 

 

2、 子类对象可以调用父类中的成员,但是父类对象永远都只能调用自己的成员。

 

3、is:表示类型转换,表示能够转换成xxx..如果能够转换成功,则返回一个true,否则返回一个false

as:表示类型转换,如果能够转换则返回对应的对象,否则返回一个null

 

4、protected

受保护的:可以在当前类的内部及该类的子类中访问。

 

5、ArrayList 集合的长度问题

每次集合中实际包含的元素个数(count)超过了可以包含的元素的个数(capacity)的时候,

集合就会像内存中申请多开辟一杯的空间,来保证集合的长度一直够用。

 

6、Hashtable键值对集合   字典 孙 sun---孙

在键值对集合当中,我们是根据键去找值的。

键值对对象[键]=值

****:键值对集合中,键必须是唯一的,而值是可以重复的

 

Add与直接用索引赋值的区别:

ht.Add(1, "张三");

ht[1] = "吧张三干掉";

//这样可以

ht.Add(1, "张三");

ht.Add(1, "吧张三干掉");

//这样就会报错

 

7、foreach循环

 

 

System.IO

Path类 主要用来对文件的路径进行操作

File类 创建、删除、复制

 

编码:

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

第十二天

 

1、 绝对路径和相对路径

绝对路径:通过给定的路径直接能在我的电脑中找到这个文件。(只能在我的电脑中使用)

    相对路径:文件相对于应用程序的路径。

结论:我们在开发中应该使用相对路径。

 

2、 装箱、拆箱(这种操作影响系统性能)

装箱:就是将值类型转换为引用类型。

拆箱:将引用类型转换为值类型。

看两种类型是否发生了装箱或者拆箱、要看,这两种类型是否存在继承关系。

如果存在继承类型,则发生拆箱或装箱。

如果不存在继承,则不会发生拆箱或装箱。

 

键值对集合,很像Hashtable  英文叫做KeyValuePair

Dictonary<int,string>

一种更洋气的遍历方法

 

 

FileStream:

         

string path = @"D:\a.txt";

           

            //FileStream 操作字节的 操作任何类型文件

            //1、创建FileStream对象

 

FileStream fsRead = new FileStream(path,FileMode.OpenOrCreate,FileAccess.Read);

 byte[] buffer = new byte[1024 * 1024 * 5];

          

            //3.8Mb 5Mb

            //返回本次实际读取到的有效字节数

            int r = fsRead.Read(buffer, 0, buffer.Length);

           

            //将字节数组中每一个元素按照指定的编码格式解码成字符串

            string s = Encoding.Default.GetString(buffer,0,r);

            //关闭流 GC不能帮助回收的

            fsRead.Close();

            //释放流所占用的资源

            fsRead.Dispose();

            //释放流所占用的资源

            Console.WriteLine(s);

 

            Console.ReadKey();

 

1、 将创建文件流的对象写在using当中,会自动的帮助我们释放流所占用的资源。

 

写一个复制文件的函数(骚操作)

 

static void Main(string[] args)

        {

            //思路:先用流将多媒体文件读取出来,然后写入到你指定的位置

            string source = @"D:\1.Flv";

            string target = @"D:\dist\2.flv";

            CopyFile(source, target);

            Console.WriteLine("复制成功");

            Console.ReadKey();

        }

 

        public static void CopyFile(string source, string target)

        {

            //1、我们创建一个负责读取的流

            using (FileStream fsRead = new FileStream(source, 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);

 

                        //如果返回一个0,也就意味什么都没有读取到,读取完了

                        if (r == 0)

                        {

                            break;

                        }

                        fsWrite.Write(buffer, 0, r);

                    }

                   

                }

            }

        }

 

 

File、FileStream、StreamReader、StreamWriter

     处理字节    处理字符

 

2、 实现多态的手段

//概念:让一个对象能够表现出多种的状态(类型)

 //实现多态的3种手段:1、虚方法 2、抽象类 3、接口

 

 Person p1= new Student();

Person p2= new Teacher();

p1.SayHello();

p2.SayHello();

//表现出来不一样

这就是多态

 

1)、虚方法

步骤:

1、 将父类的方法标记为虚方法,使用关键字virtual,这个函数可以被子类重新写一遍。 子类方法中写public overwrite xxxx ….

 

 

 

2)、抽象类

 

签名:返回值和参数类型叫做方法的签名。签名相同即返回值和参数类型相同。

 

当父类中的方法不知道如何去实现的时候,可以考虑将父类写成抽象类,将方法写成抽象方法。

 

public abstract class aaa

{

  public abstract void Bark();

//这里一定是没有方法体的

}

  1. 抽象成员必须标记为abstract,并且不能有任何实现。
  2. 抽象成员必须在抽象类中。
  3. 抽象类不能被实例化。
  4. 子类继承抽象类后,必须把父类中所有的抽象成员都重写。

(除非子类也是一个抽象类,则可以不重写)

  1. 抽象成员的访问修饰符不能是private。
  2. 在抽象类中可以包含实例成员。

并且抽象类的实例成员可以不被子类实现。

  1. 抽象类是有构造函数的。虽然不能被实例化。
  2. 如果父类的抽象方法中有参数,那么。继承这个抽象父类的子类在重写父类方法的时候也必须传入相应的参数,如果抽象父类的抽象方法中有返回值,那么子类在重写这个抽象方法的时候也必须要传入返回值。(即:签名必须相同)

 

 

如果父类中的方法有默认的实现,并且父类需要被实例化,这时可以考虑将父类定义成一个普通类,用虚方法来实现多态。

如果父类中的方法没有默认实现,父类也不需要被实例化,则可以将该类定义为抽象类。

 

    //总结:

    //List<T> 与ArrayList区别 不会发生拆装箱

    //Dictionary<Tkey,Tvalue> 与Hashtable区别 不会发生拆装箱

    //FileStream 操作字节 必须要掌握

    //StreamReader和StreamWriter 操作字符

    //File 都可以操作

 

 

 

 

 

 

 

 

 

 

 

 

 

第十三天

1、 C#中的访问修饰符

public:公开的公共的

private:私有的,只能在当前类的内部访问

protected:受保护的,只能在当前类的内部以及该类的子类中访问

internal:只能在当前项目中访问。在同一个项目中internal和public权限相同。

protected internal: protected+ internal

 

1)、能够修饰类的访问修饰符只有两个:public、internal。

2)、可访问性不一致。

子类的访问权限不能高于父类的访问权限,会暴露父类的成员。

 

2、 设计模式

设计这个项目的一种方式。

 

3、简单工厂模式:

工厂返回的是父类,父类中装的是子类对象,屏蔽了各个对象的差异。

 

4、值类型在赋值的时候,传递的是这个值的本身。

 引用类型在复制的时候,传递的是对这个对象的引用。

例一:

例二:

static void Main(string[] args)

        {

            Person p = new Person();

            p.Name = "张三";

            Test(p);

            Console.WriteLine(p.Name);

            Console.ReadKey();

        }

 

        public static void Test(Person pp)

        {

            Person p = pp;

            p.Name = "李四";

        }

字符串虽然也是引用类型,但是:

            string s1 = "张三";

            string s2 = s1;

            s2 = "李四";

            Console.WriteLine(s1);

            Console.WriteLine(s2);

            Console.ReadKey();

由于字符串的不可变性,改变一个,其他的不受影响,因为字符串是重新开辟空间。

 

 

static void Main(string[] args)

        {

            int number = 10;

            TestTwo(ref number);

            Console.WriteLine(number);

            Console.ReadKey();

        }

 

        //int n = number;

 

        public static void TestTwo(ref int n)

        {

            n += 10;

        }

 

 

 

没使用ref时

 

&number

0x009bf1f8

    *&number: 10

&n

0x009bf1a8

    *&n: 20

 

 

 

使用了ref后,操作的栈地址都是相同的。

(ref的作用:将值传递改变为引用传递)

&number

0x010ff0c8

    *&number: 10

 

 

0x010ff0c8

*&n: 20

 

1、 序列化:就是将对象转换为二进制

反序列化:将二进制转换为对象

作用:传输数据。

 

序列化:

1)、将这个类标记为为可以被序列化

   //  要将p这个对象 传输给对方电脑

            Person p = new Person();

            p.Name = "张三";

            p.Age = 19;

            p.Gender = '男';

            using (FileStream fsWrite = new FileStream(@"D:\a.txt", FileMode.OpenOrCreate, FileAccess.Write))

            {

                //开始序列化对象

                BinaryFormatter bf = new BinaryFormatter();

                bf.Serialize(fsWrite, p);

            }

            Console.WriteLine("序列化成功");

            Console.ReadKey();

 

 

         //   接收对方发送过来的二进制 反序列化成对象

            Person p;

            using (FileStream fsRead = new FileStream(@"D:\a.txt", FileMode.OpenOrCreate, FileAccess.Read))

            {

                BinaryFormatter bf = new BinaryFormatter();

                p = (Person)bf.Deserialize(fsRead);

                Console.WriteLine(p.Name);

                Console.WriteLine(p.Age);

                Console.WriteLine(p.Gender);

                Console.ReadKey();

            }

 

2、 partial部分类

 

3、 sealed密封类 不能够被其他类继承,但是可以继承于其他类。

 

4、 接口

 

[public] interface I…able

{

  成员;

}

 

接口是一种规范。

只要一个类继承了接口,这个类就必须实现这个接口中所有的成员。

 

为了多态。接口不能被实例化。

也就是说,接口不能new(不能创建对象)

 

接口中的成员不能加”访问修饰符”,接口中的成员访问修饰符为public,不能修改。

 

(默认为public)借口中的成员不能有任何实现(“光说不做”),只是定义了一组未实现的成员。

 

接口中只能有方法、属性、索引器、事件,不能有“字段”和构造函数。

 

接口与接口之间可以继承,并且可以多继承。

 

接口并不能去继承一个类,而类可以继承接口(接口只能继承于接口,而类就可以继承接口,也可以继承类)

 

实现接口的子类必须实现该接口的全部成员。

 

一个类可以同时继承一个类并实现多个接口,如果一个子类同时继承了父类A,并实现了接口IA,那么语法上必须把父类A写在接口前面。

 

 

问:

//什么时候用虚方法来实现多态?

//什么时候用抽象类来实现多态?

//什么时候用接口来实现多态?

 

答:在我提供给你的几个类中,如果可以抽象出一个父类,并且父类当中必须得写上这几个子类中共有的一个方法,你还不知道这个方法怎么实现,使用抽象类来实现多态。

 

如果这个方法可以写,并且还需要创建父类的对象,使用虚方法。

 

这几个类中找不出父类,但是这几个类都有一个共同的行为(能力),这个时候使用接口。

 

 

posted @ 2018-02-28 17:23  hh9515  阅读(210)  评论(0编辑  收藏  举报