C#学习笔记 09.01
(学习视频来自bilibili的传智播客赵老师基础教学视频)
面向过程与面向对象
这两个都是指的思考方式
面向过程
强调的是完成一个事情的动作。
面向过程的思想是自己把事情都做了。
比如将大象塞进冰箱需要三步
- 打开冰箱门
- 把大象塞进冰箱
- 关上冰箱门
面向对象
面向对象是找一个对象把事情解决掉。
比如把大象放进冰箱这件事,涉及到了三个对象,自身,大象,冰箱
在这件事情中,自己是主动的,大象是被动的,冰箱也是被动的
因为需要开门,塞大象,关门三个动作,涉及次数最多的是冰箱
所以我们将冰箱视为对象。
- 冰箱门可以被打开
- 大象可以被塞进冰箱
- 冰箱门可以被关闭
这样一来,不论主体是谁,都不需要关心,所以通用性会更强。
面向过程的思想,会造成更改的时候,或者拓展的时候很麻烦。
云里雾里中……
再来个例子
去中关村买电脑
面向过程的思想
- 查找要买的电脑的型号和价格
- 坐车去中关村
- 跟服务员讨价还价
- 被骗……
这种情况下,如果换个人想买的话,就需要重新表达一次这个过程
面向对象的思想
找一个大牛
- 大牛去查找要买的电脑的型号和价格
- 大牛坐车去中关村
- 大牛跟服务员讨价还价
- 大牛被骗……
这样不论是谁买电脑,买电脑的过程都简化成了 给钱,至于给完钱之后的事情就不用操心了
面向对象的三大特征
封装、继承、多态
比如 Console.WriteLine()
我们不需要知道是什么原理,只需要知道它能实现什么就行了
描述一个对象的时候一般分为两部分:特征,行为
对应在程序中使用的就是:属性、方法
对象是需要具体到某个,看得见摸得着的才是对象……
对象的共性指的也就是相同的属性和方法。
我们把具有相同属性和行为的对象进行进一步的封装,提取出一个名叫类的概念。类就是一个模子,确定了对象应该具有的属性和方法。
类,就不再是一个能看的见摸得着的东西了,而是一个模子,确定了其下的对象所拥有的属性和方法。
这部分如果要是玩过 Revit 的话就会比较好理解一些,比如墙就是一个类,画出来的墙就是对象。
试着写写。。。
新建好一个类之后,一般就会先加一个 public 变成公开的,也就是可以在其他平行的类中进行调用的。
关键字
class 类 enum 枚举 struct 结构
访问修饰符
public 公开的
pravite 私有的(外部不能访问到)
类的语法
【访问修饰符】class 类名
{
类的成员(字段,属性,方法)
}
访问修饰符 public。
当我们写好了一个类之后一般需要创建一个属于这个类的对象(静态类除外static修饰的),这个过程称为类的实例化。
使用关键字 new
字段与属性
字段一般用来存储内容,如果公开,则可以在外可以进行赋值
写方法的时候如果用 static 修饰就是静态的,不加就不是静态的。
写方法的时候用 this 就代表是当前类的对象 将需要的字段 “.” 出来。
this 这个东西貌似可加可不加,但是一旦在类内有同名变量的存在,就容易出现混淆,得不到我们想要的结果,但是。。。。没人会声明和字段同名的变量的吧。。。。
类是不占内存的,只有当类被实例化了之后才会占内存。
在一个类中,应该给每个字段配备一个属性,属性的本质是由 get 和 set 两个方法,用来取值和设值。为什么这样呢,是因为字段如果被公开之后,可以随意的更改,这在某些情况是不合适的。所以我们引入一个叫属性的东西,将所有字段都私有,利用属性控制外部是否可以对字段进行赋值或者读取。
虽然最终通过属性我们还是在给字段进行赋值和取值,但是利用属性我们让这个访问过程可控,可选择。
属性的用法
string _name; //声明字段的时候不加 public 修饰,这样字段就是私有的,不能被外部访问
public string Name // 配合一个公开的方法(属性,这个方法其实比较特别,因为不是()结尾的)
{
get { return _name; }
set { _name = value; }
}
如果我们在 set 这个方法中设定一些判定进去,那么就会实现在赋值时进行一些条件判定,让我们对应的字段的取值区间有一个管控。
而取值的时候可以把一些判定卸载 get 里面,从而实现对取值这个过程也有所把控。
有 get 无 set 那么就只读了(只读属性)
无 get 有 set 那么就只写了(只写属性)
不过这俩至少是要有一个的,俩都有也是可以的。。。
对象
类名 对象名 = new 类名();
给对象的每一个属性进行赋值的过程,称为对象的初始化。
如果属性特别多,这个过程就会变得很麻烦。
所以我们引入一个东西:构造函数
构造函数
构造函数其实就是一个函数(方法)
语法:
public 构造函数名 () //一定是 public
{
代码;
}
构造函数的特点:
- 构造函数的函数名一定是和类名一样的
- 构造函数没有返回值,也不需要写 void
- 构造函数可以重载
- 类有一个无参数的默认构造函数,当写了一个新的构造函数之后,这个默认的构造函数将不再默认生成,如果还需要一个无参数的构造函数,那么需要自己写一个。
构造函数的作用其实是将我们为各个属性赋值的过程写在这个构造函数里面,然后让我们的对象在声明的时候可以像一个方法那样,把属性都写在括号里面,从而简化一个个点出来,再赋值的过程。
调用构造函数的时候一定就是实例化对象的时候,因为 new 做了三个事情:
- 在内存 堆 中开辟空间
- 在开辟的 堆 空间中创建对象
- 调用对象中的构造函数
值类型和引用类型
值类型:int double char bool decim struct enum
引用类型: string 数组 自定义类
值类型是存储在 栈 中的
引用类型是存储在 堆 中的
变量名指向的都是栈,而值类型通过变量名就直接找到了值。
引用类型也是变量名指向栈,不过栈中存储的是对应的值所在堆中的位置,需要顺着这个存储的位置再去找到对应的值。
静态和非静态
- 看一个成员是否是静态的,就看是否以 static 修饰。
- 以 static 修饰的就是静态的,反之则是非静态的。
- 在一个非静态类中,是可以出现静态成员的,比如控制台一打开的这个类是非静态的,而 mine 函数是静态的。
- 非静态的函数中可以访问到非静态成员和静态成员。
- 静态函数中不可以访问到非静态成员。
- 静态类中也不可以出现非静态成员。
- 通过 对象 . 能点出来的都是实例成员(实例成员需要用对象去调用)
- 静态方法调用 类名 . 方法名就可以(静态成员要用类名去调用)
- 静态类不可以实例化,成员都类名点出来就行了。
什么时候用静态类,什么时候用非静态类
如果一个类是工具形的,那么写成静态类比较合适(面向过程的思路)
静态类最大的好处是资源共享,静态类其实是占内存的,比如登陆完QQ之后通过QQ打开各种链接都不需要重新登陆,账号密码大概就是存在静态类中了。
非静态类在面向对象的思路中会比较有用……
C# 垃圾回收
C# 不需要手动去释放资源
当我们程序结束之后,GC会看一下空间中有没有未被指向的,找到之后就会销毁。。
析构函数
在程序调用的时候不会运行,而是程序结束的时候自动调用
声明时候前面加 ~
因为 C# 不需要我们手动释放资源,所以一般没啥用。。。
写个小类,举个例子
一个车票类,距离决定价格,一旦确定了就不可更改。
using System;
namespace _13_MianXiangDuiXiang
{
class Program
{
static void Main(string[] args)
{
Ticket chePiao1 = new Ticket(50.3584564);
Console.WriteLine(chePiao1.JiaGe);
Console.ReadKey();
}
}
public class Ticket
{
double _juLi, _jiaGe;
public double JuLi
{
get { return (int)_juLi; }
}
public double JiaGe
{
get { return (int)(_jiaGe * 100 + 0.5) / 100.00; }
}
public Ticket(double juLi)
{
this._juLi = juLi < 0 ? 0 : juLi;
jiSuanJiaGe();
}
void jiSuanJiaGe()
{
this._jiaGe = this._juLi * 1;
if (this._juLi <= 100)
{
}
else if (this._juLi <= 200)
{
this._jiaGe = this._jiaGe * 0.95;
}
else if (this._juLi <= 300)
{
this._juLi *= 0.9;
}
else
{
this._juLi *= 0.8;
}
}
}
}
在一个项目中想用另一个项目中的类
- 添加引用
- 添加命名空间
- 类应该是 public 的
再回忆一下访问修饰符
public 公开
private 默认修饰类成员的修饰符,实现类内私有
internal 默认修饰类的修饰符,实现程序集外不可访问
字符串
因为是引用类型,所以不断重新赋值会不断开辟新空间,占用大量内存。
如果我们需要对一个字符串进行大量的重新赋值或者拼接操作,那么建议使用 StringBuilder
因为对 string 不断重复赋值需要不断的重新开空间(每一次赋值都像是实例化了一个新对象),所以运行效率也很低。而 StringBuilder 是一个对象,利用其方法对其进行修改的时候都是在他原有的空间上进行操作,所以效率比较高;
字符串 string 这个东西有点像一个由 char 组成的数组,所以可以用 [ ] (索引)进行访问,但是直接这么访问的时候是只读的,如果我们想修改,需要将其转化成真正的 char 数组:
char【】 起个名 = 字符串名.ToCharArray();
这样一来就可以利用索引进行访问并修改,改好了之后再拼回去;
str = new string (char)
字符串这个东西最重要的就是他这一坨属性与方法
- ToCharArray() 方法 将字符串变成一个 Char 数组
- Length 属性 返回字符串长度
- ToUpper() 方法 将字符串转成大写
- ToLower() 方法 将字符串转成小写
- Equals() 方法 比较两个字符串是否相同,这个方法有重载,以一个枚举类型来确定是否忽略大小写。
- Split() 方法 分割字符串有6个重载,以一个 char 数组描述存储分割符号 ,返回一个字符串数组,可以补充一个枚举型参数来确定是否忽略空值。
- Replace() 方法 将出现的指定字符替换成另一字符(当然,后面用正则表达式会比较多,然而我现在还不知道啥是正则表达式)
- Substring() 方法 截取字符串 有点像 Excel 的 mind
- StartsWith() 方法 判断是否以某一字符串开头
- EndsWith() 方法 判断是否以某一字符串结尾
- Contains() 方法 判断字符串中是否包含后裔字符串
- IndexOf() 方法 返回某一字符串首次出现的索引,如果找不到就返回 -1
- LastIndex() 方法 返回某一字符串最后出现的索引,如果找不到就返回-1
- Trim() 方法 返回一个去掉了前后空格的字符串,值得一提的是位于字符串中间的空格是不能被去掉的。相应的还有两个加个 start 或者 end 的方法,是去掉字符串前面的空格和后面的空格。
string 这个类还有一些静态方法
string.Join() 将一堆字符串以特定分割符连接在一起,类似 Excel 里面的 textjoin
那么问题来了,怎么解决中间的空格呢?
这就需要前面那个 Split 方法,将空格视为分隔符,也就把空格去掉了,灵活运用一下。往往灵活运用这个东西是最神奇的。。。