一. 交换两个数据的值:
1. //创建一个临时变量
//int temp;
//temp = num1;
////用num2的值覆盖掉num1
//num1 = num2;
////将存储在临时变量中的nu1取出来赋值给num2
//num2 = temp;
2.不使用临时变量:
num1 = num1 + num2; //11
num2 = num1 - num2; //11-6=5;
num1 = num1 - num2; //11-5=6
二:定义方法:
访问修饰符 返回值类型 方法名称(参数){方法体}
1. 访问修饰符:public internal protected private
a) public :使用它来定义的成员可以在所有类里面使用(不同项目需要先引用)
b) private:私有的,只有在当前类中可以使用
c) protected:使用它来定义的成员只有在当前类或者当前类的子类中使用。
d) internal:在当前整个程序集中可以使用
2. 返回值类型
a) void:没有返回值.(可以写return(可以提前终止方法),但是不能return具体的值)
b) 非void,必须返回相应类型的值(任何类型都可以做为方法的返回值类型)
3. 方法名称:
a) Pascal命名法,每一个单词的首字母需要大写
b) 不能使用关键字(int double bool string …..class namespace)
c) 不能包含_以外的特殊符号,不能以数字开头 java(_ $)
4. 参数
a) 形参:只是一个参数的声明,说明调用的时候需要传递过来的值(类型一致,个数一致,顺序一致)---形参是实参的规范。Params 帮你创建一个数组,数组的长度由你传递过来的值个数决定 int[] arr=new int[0]
b) 形参是实参的规范 调用方法相当于实参为形参赋值(赋值的只是实参的值的副本 形参也是一个变量,需要独立的存储空间)
三:静态与非静态之间(成员)的调用方式:
1. 在单个类中:静态不能直接调用非静态成员(属性,方法),只能通过实例化的对象调用。
2. 在单个类中:静态可以直接调用静态成员
3. 在单个类中:实例成员可以自由调用静态成员(银行 vip---非vip)
4. 在不同类中:实例成员通过类的对象调用,而静态成员通过类名调用。const
5. 为什么静态不能直接调用非静态成员:静态成员与类一起加载,在加载静态成员的时候,非静态成员还没有生成。
四:C#Main方法的四种形式:
1. static void Main(string[] args)
2. static void Main()
3. static int Main(string[] args)
4. static int Main(s)
五:可变参数:params:它是一个数组类型的变量。会自动根据用户传递的值生成一个数组(注意传递的值需要同一类型。)
1. 可以根据需要传递0 个或者多个参数。
2. 可变参数必须放在参数列表的最后
3. 如果没有传递任何参数,需要做一个判断,不然可能造成索引越界的错误。
4. 案例
private static int GetMax(string name,params int[] arr)
{
// int[] arr1 = new int[0];
if(arr.Length<=0)
{
return -1;
}
int max = arr[0]; //对于数组的比较不能直接将最大或者最小值赋值为0,应该赋值:max=arr[0]
for (int i = 0; i < arr.Length; i++)
{
if (max < arr[i])
{
max = arr[i];
}
}
return max;
}
六:c#中方法参数的默认值:
1. 参数可以赋默认值
2. 有默认值的参数必须在整个参数列表的最后
3. 在调用方法的时候可以选择为参数赋值,如果赋值会覆盖默认值,如果没有就直接使用默认值。
4. 案例:
private static int GetMax(string name,string sex=“男”)
七:代码规范:
1. 注释:
a) // 单行注释
b) /**/:多行注释
c) ///文档注释:可以生成提示
2.变量命名:camel:第一个单词首字母小写,后面每一个单词的首字母大写
3.对于类和方法:每一个单词首字母都要大写。
4.代码需要缩进与对齐。
记住:
1.在企事业里面可能这有这样的建议:规定所有自定义变量都使用 _开头
2.变量的名称一般是名词:说明这个变量有什么用
3.方法:一般是动词,说明这个方法可以执行什么样的操作
4.在定义变量的时候,长短不是关键,意义才是关键
八:数组:所有存储结构中效率最高的。数组的长度一旦确定就不能再修改。因为它在内存中是一串连续的空间,它是通过下标直接计算出需要的值的位置,也说了说明了索引为什么不能越界
1. 如何定义:类型[] 名称=new int[长度];
2. 数组里面只能存储同一类型的值
3. 在c#中,[]只能写在类型后面
4. 一般需要确定长度或者值,可以同时指定,但是长度与值的数量需要对应。
5. 因为数组长度固定,所以对数组的操作一般可以考虑使用for循环结构!
6. 数组元素的添加:
1. int[] arrScore = new int[5]{1,2,3,4,5};
2. arrScore[0]=1;//如果只为其中某一些下标对应的数组元素赋值那么其它的就会有默认值。
3. for (int i = 0; i < arrScore.Length; i++)
{
arrScore[i]=值;
}
7. 数组元素的修改:
ArrScore[i]=值;
8. 数组元素的遍历:
for (int i = 0; i < arrScore.Length; i++)
{
if(arrScore[i]!=null) //对于引用类型象数组才有用(string)
{
}
Console.WriteLine(arrScore[i]);
}
9. 数组元素的删除:
//1.先确定需要删除的元素的位置(下标索引)--需要循环遍历
for (int i = 0; i < arrScore.Length; i++)
{
if(arrScore[i]==num)
{
position = i;
}
}
Console.WriteLine("position:"+position);
//2.将这个值以后的所有值都往前面移动一位: arr[i] = arr[i + 1];//需要注意索引是否越界
for (int i = position; i < arrScore.Length-1;i++ )
{
arrScore[i] = arrScore[i + 1]; //将后面的元素值覆盖前面元素值
}
//3.清空最后一个值:注意不同数组类型的默认值,这里因 整型数组的默认是0.所以这里只是清空到0
arrScore[arrScore.Length - 1] = 0;
10. 练习:创建长度动态可以变化的数组—为集合做准备:
int[] arr=new int[2];
/// <summary>
/// 数组中已经存储的元素的个数
/// </summary>
int count=0;
#region 往数组中添加元素 +void Add(int num)
/// <summary>
/// 往数组中添加元素,约定:数组中的元素不能是0
/// </summary>
public void Add(int num)
{
if(count==arr.Length) //当数组中存储的元素的个数刚好等于数组的长度的时候,数组就需要扩容
{
int[] newArr=new int[arr.Length*2]; //数组的长度不可能动态变化,所以只能创建一个新的数组,长度是原始数组的两倍
//将原始数组的值移动到新数组中
arr.CopyTo(newArr, 0);
//重新指定数组的地址引用
arr = newArr;
}
arr[count] = num; //第一次的时候count=0
count++; //添加一个元素,个数值增1
}
#endregion
九:二维数组和交错数组
1. 二维数组
a) 定义:int[,] arr=new int[3,4];//必须指定二个维度的值
b) 遍历:通过getLength取得维度的长度值:代码如下:
for (int i = 0; i < twoDArr.GetLength(0);i++ )
{
for (int j = 0; j < twoDArr.GetLength(1);j++ )
{
Console.Write(twoDArr[i,j]+" ");
}
Console.WriteLine();
}
2. 交错数组
a) 交错数组的本质就是一个一维数组,只不过这个数组里面的每一个元素值又不一个数组
b) 交错数组的元素需要先实例化,因为数组是一个引用类型的变量,需要先实例化再使用,如果没有实例化会出现 “未将对象引用设置到对象的实例”
c) 因为交错数组的每一个元素类型是数组,所以遍历的时候就可以按遍历一维数组的方式一考虑,代码如下:
int[][] arr=new int[2][];
//数组的每个元素又是一个数组对象,所以需要先实例化数组对象
arr[0] = new int[] { 1, 2, 3 };
arr[1] = new int[] { 1, 2, 3,4,5,6,7,8,9 };
for (int i = 0; i < arr.Length;i++ )
{
for (int j = 0; j < arr[i].Length;j++ )
{
Console.Write(arr[i][j]+" ");
}
Console.WriteLine();
}
十:return:
1. 可以返回一个变量---当方法有返回值类型的时候
2. 还可以中止当前方法
3. 如果在异常处理结构里面,不管是否之前运行到return,finally的代码都会被执行。
十一:变量的作用域:
1. 全局变量(成员变量):类的属性:public private
2. 局部变量:在方法体内定义的变量---作用域只是在方法体内,出了方法体就不能再使用,如果一定需要使用,有三个方法:
a) 可以做为参数传递
b) 还可以做为返回值
c) 还可以做成类的成员变量---静态成员变量不是很安全
十二:方法重载:系统会根据调用方法传递的实参来自动匹配拥有相应形参的方法。
1. 方法名称一致,但是方法的参数不一致(参数的类型不一致,参数的数量不一致),只要满足其中一个就构成重载的条件(如果两个都满足那一定构成重载的条件)--与方法的返回值无关,也与方法的访问类型无关。
2. 这里的方法重载是指同一个类中方法重名,参数不一致的情况。
3. 作用:同一个名称,多种响应结果(与你的参数有关系),解决了命名烦恼。
十三:普通的值传递和ref传递参数:
1, 普通的值做为参数传递的时候,传递是其实是值的副本,对副本的操作不影响到原始值
2, 所谓值的副本是指存储在栈空间里面的内容的副本。对于值类型的变量而言,栈空间里面存储的就是值本身,但是是对于引用类型的变量而言,栈里面存储的是引用地址。
3, 如果使用了ref/out传递参数,那么传递是存储了值类型数据的栈空间地址,会造成原始值和方法形参指向同一个地址空间,在方法里面进行的修改会影响到原始值。
4, Ref侧重于输入,out侧重于修改。如果使用out传递参数,那么:1.在方法里面必须对传入的参数进行值的修改。2.out传入参数可以不为实参赋值
十四:面向对象:一种分析问题的方式,当你需要完成一个功能,就去找能够实现这个功能的对象。对象来自于类。而类来自于现实生活中需要模拟的对象
1. 封装:属性封装,方法 ,类,程序集都是封装
2. 继承
3. 多态
十五:this:当前创建的对象是那一个,this就代表当前对象
十六:类的对象的关系:
1. 类是用来描述事物的,是模型,是一个概念,它的成员:属性(描述的对象的特征) 方法(描述的对象的行为)
2. 对象是类的实例,是具体的存在。
3. 类的成员有两种:字段/属性,行为/方法:
十七:构造函数:它就是构建类的对象用的。一个特殊的方法:返回值,重载,传递参数
1. 没有返回值,名称和类名一致,任何成员(除了构造函数)名称都不能和类名一致
2. 每一个类都会有一个默认的无参的构造函数。
3. 构造函数不能像普通方法一样显示调用,只能创建对象的时候调用,以及子类base(当前类也可以this)显式的调用父类的构造函数
4. 如果人为添加了带参的构造函数,那么系统默认生成的无参构造函数会自动消失。所以一般我们应该养成一个习惯:创建好一个类,立刻为其添加无参的构造函数。
5. 构造函数也可以重载。 方法名称一致, 参数不一致(类型不一致,个数不一致)
6. 使用this调用当前类的构造函数:
class Student:Person //默认是当前程序集中可以使用而已
{
//任何一个成员的名称都不能与类名一致,除了构造函数
private void Student()
{
}
/// <summary>
/// 创建子类对象,默认会调用父类的无参的构造函数,除非你指定调用父类的那一个带参的构造函数
/// </summary>
public Student(string name,string sex,int age)
{
this.Name = name;
this.Gender = sex;
this.Age = age;
}
public Student(string name, string sex, int age,string no):this(name,sex,age)
{
this.StuNo = no;
}
7. 使用base调用父类构造函数:
class Student:Person //默认是当前程序集中可以使用而已
{
public Student(string name,string sex,int age):base(name,sex,age)
{
}
}
十八:属性:对象的特征:
1. 属性的作用:封装字段,提供一种过滤的方式 get是指获取(读取),set(赋值)
2. 属性的本质:两个方法 Set() Get()
3. 属性封装的实现:value相当于一个隐藏的参数,它就是用户赋的值。
4. 如果过滤:做验证:
set
{
if (value > 0 && value <= 100)
{
age = value;
}
else
{
age = 18;
}
}
5.自动属性:public int Age{get;set;}
十九:继承
1. 可以将多个类的公共的属性和行为提取到一个公共类中,这个公共类就可以称为父类。
2. 父类只能包含子类共有的成员,不是所有子类都拥有的成员不能在父类中创建。
3. 子类继承父类,必须能够实现父类中所规范的所有成员,如果不能实现所有成员继承关系就不能成立。
4. 使用 : 实现继承。
5. 如果子类拥有与父类重名的属性和方法的时候,子类的属性和方法会将父类的覆盖掉。如果需要显示调用父类的成员,则需要使用base.不然会默认调用this.
6. 创建一个子类对象,默认先会调用父类的无参的构造函数。如果父类没有无参的构造函数,会报错。
7. 创建子类对象的时候,也可以指定调用父类的带参的构造函数。Base
8. 子类会继承父类的所有成员,但是只能使用公共的,而私有成员不能使用。
9. 继承有三大特性:
a) 单根性:一个类只能继承自另外一个类
b) 传递性:一个类是另外一个类的子类,也可能同时是另外一个子类的父类
c) 单向性:不能循环依赖
二十:多态的实现:同一种操作,多种不同的相应。
1. 声明父类变量,实例化子类对象 Person per=new Student();
2. 父类创建虚方法,子类做重写。---子类重写父类的方法
3. 虚方法提供了默认的实现,子类可以不重写,如果子类没有重写,那么就默认调用父提供的方法实现。如果子类重写了,系统会自动判断子类类型,调用子类的重写方法 -----这就是多态
4. 多态的核心在于:子类可以替换父类,原因:
a) 子类拥有父类所规范的所有属性和方法
b) 子类还可以拥有自己特定的属性和方法
c) 父类对象可以完成的功能子类对象都可以完成
d) 所以,子类可以替换父类。如Person per=new Student();
e) Per本质是一个子类对象,但是编译器不知道,所以per只能调用父类里面规范的成员。如果做强制转换,则可以调用到子类成员。
5. 如果需要系统自动判断类型,达到多态的目的需要做以下事情:
a) 实现继承---- 子类是父类
b) 父类中声明虚方法(抽象方法),子类做重写(override)
c) 声明父类变量,实例化子类对象 / 父类做为参数,传入子类对象。
6. 多态实现了避免反复的判断才能达到的效果,这也是多态的好处。方便以后的代码功能的扩展
7. 抽象方法:abstract:只是方法的声明,没有方法体,不能包含{},但是必须包含 ;
8. 抽象方法的特点 :
a) 只有声明
b) 它必须在抽象类里面(抽象类里面不仅仅只包含抽象方法)
c) 所有子类必须重写父类的抽象方法,除非子类它也是抽象的,抽象类里面可以包含未实现的抽象方法 。
d) 抽象方法更多是一种规范,它规范了子类的行为。---接口
e) 抽象类不能实例化。
9、多态的使用方式:
1.声明父类变量,实例化子类对象 ---创建父类类型集合
2.以父类做为方法的返回值,返回子类对象---简单工厂:用户需要频繁的得到不同的单个子类对象的时候
3.以父类类型做为参数,传入子类对象
10.如果父子类的方法参数不一样,那它是重载而不是重写。
11.重写要求方法的签名完全一致:访问类型一样,返回值类型一样,方法名称一样,方法的参数一样,只是方法体的实现过程不一样
二十一:简单工厂:制作产品:根据用户的需要,每一次制作出一个产品
public static Operator CreateOperator(string type)
{
Operator opt = null;
if (type == "+")
{
opt = new OpAdd();
}
if (type == "-")
{
opt = new SubStract();
}
if (type == "*")
{
opt = new Multi();
}
if (type == "/")
{
opt = new Div();
}
return opt;
}
二十二:is/as
1. is用来判断引用类型变量的变量是否是某一种类型
2. as:来做强制转换,一般()转换,如果不成功,就会报异常,as如果转换不成功会返回NULL,以后,如果不想抛出异常,可以使用as
二十三:软件设计中的几个原则:
1. 封装变化点
2. 开放封闭原则(对修改封闭,对扩展开放)---添加单独的功能—添加单独的方法; 添加单独的角色---添加单独的类
3. 高内聚,低藕合
二十四:接口:
1. 接口本质就是一个抽象类,在接口里面就是声明一系列的属性,方法,索引器和事件。从反编译工具可以看到接口是抽象类---抽象的不能实例化,只能被实现;接口是一个类,说明它也是一种数据类型,可以通过接口创建出接口对象(所谓的接口对象其实是实现该接口的类的对象)
2. 创建一个接口对象,其实就是创建一个 实现了这个接口的类 的对象
3. 如果某个类没有实现某个接口,那么这个这个类的对象不能强制转换为接口类型。
4. 接口不要求实现接口的类有某种关联(只需要有共同的行为),也就是说不要求他们要父子继承关系,方便了用户的拓展。 父类---虚方法/抽象方法---子类重写---多态
5. 在公司里面,接口更多的认为是一种规范。
6. 使用接口的方式:
a) 声明接口类型的变量,存入 实现了接口的类的 对象
b) 以接口类型做为参数,传入 实现了接口的类的 对象
7. 显示调用接口:显示实现接口的方法需要接口类型的对象才能调用。因类它默认是私有的,而且还不能人为修改为public
a) 如果声明的类型就是接口类型,那么可直接调用
b) 如果声明的类型是实现了接口的类的类型,那么就需要做强制转换。
8. 接口解决了通过继承实现多态的三个问题:
a) 通过继承实现多态只能是单继承,但是接口可以多实现
b) 通过继承实现多态的类必能够提取出共同的父类,也就说明这些类之间需要有共同的特征和行为。但是接口只需要它们有共同的行为。
c) 通过实现了继承的类做为参数传递,会传递类的对象的所有属性和方法,但是通过接口做为参数,只能调用到接口中声明的方法和属性。---更加安全
二十五:结构
1, 结构是值 类继承自ValueType型
2, 它的使用方式和类很相似,但是也有一些区别:
a) 结构中不能为字段赋值
b) 结构不能包含显式的无参数构造函数
c) 如果通过构造函数为结构成员赋值,只能赋值给字段。
d) 如果不使用new来创建结构,那么就必须为结构中的字段赋值。
e) 结构是一个值类型,所以在做为参数传递的时候传递是值的副本,与引用类型不一致
f) 值类型做参数传递的时候会开辟新的空间来存储值,同时在同引用类型做转换的时候 需要 装箱和拆箱操作,消耗系统资源。
二十六:析构函数:
1. 每个类只能有一个,不能显示调用
2. 不能有访问修饰符,是系统自动调用的
3. ~Student()
{
Console.WriteLine("我调用了析构函数");
}