C#OPP入门

第一章 深入.NET框架

一 、 .NET框架体系结构

1.NET框架的结构

.NET框架也就是.NET FrameWork,主要包含公共语言运行时(CLR)和框架类库(FCL)。CLR包含两个组成部分:

CTS:通用类型系统,如C#中的整型是int,而VB.NET中的整型是Integer,通过CTS我们把它们两个编译成通用的类型Int32。

CLS:公共语言规范,如在C#中命名是区分大小写的,而在VB.NET中不区分大小写,这样CLS就规定,编译后的中间代码除了大小写之外还要有其他的不同之处。

MSIL:中间语言,vs统一能认识的规范语言。

FCL:框架类库,有些已经被封装好的功能的集合。

二、类和对象

1.类和对象的概念

类: 具有相同特性和行为的对象组成的集合就是类,实际上类就是一个数据类型。

对象:一个具体存在的实物,世间万物皆为对象。

2.类和对象的关系:

类是对象的抽象化,对象是类的实例化。

public class Student{}  //这是一个学生类
static void Main(string [] args)
{
    Student s1=new Student();  //类的实例化-->对象
}

3.类的成员:

字段:一般把类或结构中定义的变量和常量叫字段,建议私有化。

属性:get只读属性,set只读属性,可以同时拥有,也可以只存在其中一个。

方法:语法:访问修饰符 返回值类型 方法名(参数列表){}

public class Student
{
    private string _name;  //字段
    public string Name { get => _name; set => _name = value; }  //属性
    //方法
    public void SayHi(){}
}

分为两种:

静态方法:使用static关键字来标识静态方法。

非静态方法:同样,非静态方法就是不使用static关键字修饰。

区别:1.静态方法不能调用非静态方法。

2.调用:静态方法只能通过类名点方法名来访问,不能通过对象名点方法名访问。

非静态方法的调用可以通过对象名点方法名访问。

public class Employee
{
    public static void SayHi(){}
    public void ShowHobby(){}
    static void Main(string [] args)
    {
        Employee.SayHi();  //静态方法通过类名点方法名调用
        //Employee.ShowHobby();  //会报错,静态方法不能调用非静态方法
        Employee e1 =new Employee();
        e1.ShowHobby();  //非静态方法的调用可以通过对象名点方法名访问。
    }
}

①访问修饰符

public:公开的,人人都能使用。

protected:受保护的,在继承中,一般用于父类,类的受保护成员只能由类本身和类的子类内部访问,相当于有密码的WiFi。

private:私有的,只能在类内部使用,相当于自己的流量。

类的默认访问修饰符:internal

类成员的默认访问修饰符:private

②返回值类型:数据类型+void

void:void表示方法没有返回值,这时注意不能用return返回值。

数据类型:return必须存在,而且必须有值,值的类型必须和返回值类型兼容。

③参数:分为两种:

形参:定义方法时传入的参数,形参是抽象的。

实参:调用方法时传入的参数,实参的数据类型必须和形参一致,参数个数、顺序也相同。

public void Swap(){}
Swap(); //调用
public void Swap1(int a){}
Swap1("1");  //报错,实参形参数据类型不一致
Swap1(1);
public void Swap2(int a,int b){}
Swap2(1);  //报错,实参和形参数目不同
Swap2(1,2);

4.类的三大特性:

封装:将类内部构造私有化。

继承:子类继承父类。

多态:在同一范畴内,一个事物有多种形态。

第二章 深入C#数据类型

一 、值类型和引用类型

1.值类型

​ <1>、命名空间:System.ValueType;

​ <2>、保存区域:每个值类型的对象都有一个独立的内存区域用于保存自己的值,值类型数据所在的内存区域称为,值变地址也变,一个字段占用一个栈;

​ <3>、包括基本数据类型、枚举类型(enum)、结构类型(struct);

2.引用类型

​ <1>、命名空间:System.Object;

​ <2>、保存区域:引用类型存储对值的引用,数据所在的内存区域称为,值变地址不变,字段怎么改变也是原来的地址;

​ <3>、包括类、接口、数组

3.区别:

值类型:不同的变量分配不同的储存空间,一个变量的值改变不会影响另外一个变量。 //指向栈 开辟了新地址

引用类型:不同的变量分配相同的储存空间,一个引用类型的值改变也会影响另一个变量。//指向了堆 使用了原来的地址。

int heightZhang = 170;
int heightLi =heightZhang;
heightLi=180;
//int是值类型,首先系统会给heightZhang分配存储空间,然后系统还会给heightLi分配存储空间,所以改变heightLi的值不是影响heightZhang的值
int [] array = new int [5];
int []array1=array;		//这里把array的引用地址也给了array1,所以array1指向的地址和array一样,array1的值变,array的值也会变
int [] array=new int[5]
int [] array1=new int[5]	//array1开辟了一个新的空间
array1[0]=array[0];		//因为array1开辟了新的空间,而且这里是给的下标值,所以array1的值变array的值不会随着改变,因为指向的地址不同

二、结构

1、结构的定义:

访问修饰符 struct 结构名{}

2、结构的特点:

​ ①结构中可以有字段,也可以有方法,但是很少写属性;

​ ②定义时,结构中的字段不能被赋初始值

3、结构的使用:

​ ①可以不用new,直接定义结构的对象即可;

​ ②声明结构的对象后,必须给结构的成员赋初始值。

4、注意:①结构不能不支持继承;

②在没有new的时候,不能定义属性,并且不能调用方法;

③结构的构造函数必须是有参数构造函数;

④结构和类相似,但是结构属于值类型,而类属于引用类型。

//例如
public struct Student
{
    public int _id;  //字段
    public int _age;  //不能赋初始值
    public void Study(){}
    static void Main(string [] args)
    {
        Student stu;
        stu._id=111;
        stu.Study();  //报错,在没有new时候不能调用方法
    }
}

三、装箱和拆箱

1、装箱:将值类型转换成引用类型

2、拆箱:将引用类型转换成值类型

四、参数传递

1、值方式参数传递

值方式参数传递就是没有ref关键字修饰,参数可以是引用类型,也可以是值类型。

①使用引用类型作为参数

class Program
{
        static void Main(string[] args)
        {
            Number a = new Number();
            Add(a);
            Console.WriteLine(a.i);
            Console.ReadLine();
        }
        static void Add(Number a)
        {
            a.i++;
        }
    }
class Number
{
    public int i = 10;
}
//输出结果 :11

以上代码可以看出,以引用类型作为参数进行值方式传递参数时,会改变引用类型参数的值。

②使用值类型作为参数

static void Main(string[] args)
{
    //值传递
    int number = 20;
    Fun(number);//实参
    Console.WriteLine(number);
    Console.ReadLine();
}
static void Fun(int num)//形参,值类型作为参数
{
	num += 20;
}
//输出结果:20

以上代码可以看出,以值类型作为参数进行值方式传递参数时,不能改变值类型参数的值。

2.引用方式参数传递

引用方式参数传递就是有ref关键字修饰,参数同样可以是引用类型,也可以是值类型。

①使用值类型作为参数

static void Main(string[] args)
{ 
     int number = 20;
     Fun(ref number);//实参
     Console.WriteLine(number);
}
static void Fun(ref int  num)//形参
{
      num += 20;
}
//输出结果:40
class Program
{
        static void Main(string[] args)
        {
            Number a = new Number();
            Add(ref a);
            Console.WriteLine(a.i);
            Console.ReadLine();
        }
        static void Add(ref Number a)
        {
            a.i++;
        }
    }
class Number
{
    public int i = 10;
}
//输出结果 :11

以上代码可以看出,使用引用方式(用ref关键字修饰)传递值类型和引用类型参数时,参数在方法中的修改都会保留。注意,在引用方式参数传递时,实参和形参的前面都必须加上ref关键字。

第三章 使用集合组织相关数据

一、ArrayList

1.ArrayList的定义:ArrayList非常类似于数组,可以保存一组大小不固定、数据类型不相同的数据的集合。

注意:由于ArrayList不限定数据类型,所以经常涉及数据类型转换、拆箱、装箱,这是ArrayList的优势同样也是劣势。

2.命名空间:System.Collections

3.如何创建ArrayList?

ArrayList list =new ArrayList();

4.ArrayList的属性和方法

属性:Count 说明:获取ArrayList的长度

返回值类型 方法名称 说明
int Add(Object value) 添加元素,用来保存值
void RemoveAt(int index) 通过下标删除元素,下标从0开始,最后一个值的下标:长度-1
void Remove(Object value) 通过指定元素直接删除
void Clear() 清空ArrayList中的数据
//例如
ArrayList list =new ArrayList();  //创建ArrayList集合
list.Add("悠悠");  //下标为0
list.Add(17);  //存值  //1
list.Add('女');       //2
Console.WriteLine(list.Count);  //打印长度  输出:3
list.Remove("悠悠");  //删除指定元素
list.RemoveAt(2);  //运行报错,因为在这之前删除了“悠悠”,所有元素都会向前移,因此不存在下标为2的元素
list.Clear();  //清空
Console.WriteLine(list.Count);  //打印长度  输出:0

5.ArrayList的遍历

//for循环
for(int i = 0; i < list.Count; i++)
{
	Console.WriteLine(list[i]);
}
//foreach循环
foreach(Object obj in list)
{
	Console.WriteLine(obj)
}

二、List

1.定义:保存一组大小不固定,但限定数据类型的数据的集合。

2.命名空间:System.Collections.Generic

3…创建:List list =new List(); T就是Type类型的意思,表示任意一种数据类型

4.List的属性和方法

属性:Count 说明:获取ArrayList的长度

返回值类型 方法名称 说明
int Add(T value) 添加元素,当newList的时候T是什么数据类型,就只能添加什么数据类型
void RemoveAt(int index) 通过下标删除元素,下标从0开始,最后一个值的下标:长度-1
void Remove(T value) 删除指定的元素
void Clear() 清空集合中的元素
List<string> list =new List<string>();
list.Add(666);  //报错,T为string类型,666是int类型,数据类型不一致
list.Remove("你好");   //不报错,虽然我没添加到集合中,但是无影响!!!
//注意:当移除一个不存在的值,其实不会有任何错误。

5.List的遍历

//for循环
for(int i = 0; i < list.Count; i++)
{
	Console.WriteLine(list[i]);
}
//foreach循环
foreach(T item in list)
{
	Console.WriteLine(item)
}

三、ArrayList和List的区别

1.相同点:

​ ①相同的属性:Count
​ ②相同的方法:Add,Remvoe,RemoveAt,Clear
​ ③相同的遍历方式

2.不同点:

​ ①List对储存的元素类型进行约束,添加/读取值类型元素无需拆箱、装箱

​ ②ArrayList可以添加任何类型的元素,添加/读取值类型元素需要拆箱、装箱

四、HashTable

1.定义:用来保存key:value,大小不固定,并且可以添加任意数据类型的元素。

2.命名空间:System.Collections

3.创建:HashTable ht=new HashTable();

4.HashTable的属性和方法

属性名称 说明
Count 获取HashTable的长度
Keys 获取包含在HashTable中键的集合
Values 获取包含在HashTable中值的集合
返回值类型 方法名称 说明
void Add(Object key,Object values) 添加元素
void Remove(Object key) 删除指定的key
void Clear() 清空元素
bool ContainsKey(Object key) 查找指定的key是否已经存在
bool ContainsValue(Object key) 查找指定的value是否已经存在
HashTable ht =new HashTable();
ht.Add(17,"悠悠");  //键是学号,值是姓名
ht.Add(01,"喵喵");
ht[01]="菜菜"; //修改值,前提必须存在

5.HashTable遍历

//遍历key
foreach(Object obj in ht.Keys)
{
    Console.WriteLine(obj+"----"+ht[obj]);  //通过key获取值
}
//遍历value
foreach(Object item in ht.Values)
{
    Console.WriteLine(item)  //直接获取值
}
//同时获得key和value
foreach(DictionaryEnary en in ht )
{
    Console.WriteLine(en.Key+"----"+en.Value)
}

注意:1、key如果已经存在了,再次添加,会报运行错误,说明key不能重复

2、null不能作为key

五、Dictionary

1.定义:用来保存key:value,大小不固定,但是限定数据类型的数据的集合。

2.命名空间:System.Collections.Generic

3.创建:Dictionary<Tkey,Tvalue> dic =new Dictionary<Tkey,Tvalue>();

4.Dictionary的属性和方法:和Hashtable一样

5.Dictionary遍历

//遍历key和遍历value和HashTable一样
//同时遍历key和value
foreach(KeyValuePair en in dic)
{
    Console.WriteLine(en.Key+"----"+en.Value)
}

六、Hashtable和Dictionary的区别

1.相同点:

​ ①相同的属性:Count,Keys,Values;

​ ②相同的方法:Add,Remvoe,Clear,ContainsKey,ContainsValue;

​ ③相同的遍历方式

2.不同点:

​ ①Hashtable没有限定类型;

​ ②Dictionary限定了key和value的数据类型

Dictionary<string,string> dic =new Dictionary<string,string>();
dic.Add("17","悠悠");
dic.Add(01,"菜菜");  //编译错误,Dictionary中的键是string类型的,01是int类型,数据类型不一致
dic.Add(null,"明明");  //运行错误,null不能作为键
dic.Add("01","菜菜");
foreach(string en in dic.Keys)
{
     Console.WriteLine(en+"----"+ht[en]);  //通过key获取值
}
bool flag=dic.ContainsKey(02); //查找集合中是否已经存在02键,存在返回true,不存在返回false

七、泛型和非泛型的区别

泛型:限制了数据类型,比如List和Dictionary。
非泛型:没有限制数据类型,比如ArrayList和Hashtable.

八、自定义泛型类

1、泛型类的作用是将类中的属性进行限制;

2、语法:public class 类名{}

public T name; //属性

当你实例化对象时:

类名 对象名 = new 类名();

此时进入的是什么类型,类中的属性就是什么类型.

九、集合绑定dgv

1.Lis绑定dgv:this.dgv.DataSource=new BindingList();

2.Dictionary绑定dgv:

BindingSource bs =new BindingSource();

bs.DataSource=dic.Values;

this.dgv.DataSource=bs;

注意:使用Datagridview绑定集合

这里的属性需要加:{get;set;}才能绑定成功!

例如:

public string name { get; set; }

public string id { get; set; }

第四章 深入类的方法

一、构造函数

1.语法:访问修饰符 类名(){}

注意:①类似方法,只是少了一个返回值,这里是没有返回值,若为void也是错误的,void代表返回值类型为空;

②方法名有要求:跟类名一致;

③构造函数也是会被重载。

2.作用:用来new对象,兼职:给成员变量赋值

3.分类:

①显示方式:

隐式:

​ 1、就是创建一个类,没有手写构造函数的时候当前类自带的构造函数

​ 2、就是无参的构造函数

显示:自定义构造函数

②参数的格式:有参和无参

4.特点:一个类中,如果没有手写的构造函数,那么这个类中有一个隐藏的无参构造函数,如果一旦手写了构造函数,那么这个类就不存在有隐藏的构造函数。

5.给成员变量赋值的方式:

①对象初始化器

static void Main(string[] args)
{
    //对象初始化器赋值
    Student stu = new Student(){name="悠悠",age=18};
}
class Student
{
    public string name;
    public int age;
}

②点符号

static void Main(string[] args)
{
    //点符号赋值
    Student stu = new Student();
    stu.name="悠悠";
    stu.age=18;
        
}
class Student
{
    public string name;
    public int age;
}

③构造函数

static void Main(string[] args)
{
    //构造函数赋值
    Student stu = new Student("悠悠",18);   //调用Student类中的有参构造函数
} 
class Student
{
    public string name;
    public int age;
    public Student(){}  //无参构造函数
    public Student(string name,int age)
    {
        this.name=name;
        this.age=age;
    }
}

二、方法重载

1.实现多态的方式:①重载②重写

2.特点(定义):在同一个类中,方法名相同,对应的参数的下标参数类型不同。

3.重载的应用:MessageBox,Equals(可以尝试去看源代码,深入理解重载)

4.存在的意义:减少方法名的开辟,同样避免我们本身英语单词不足这一问题。

5.调用规则:根据参数【参数个数+数据类型】调用

6.注意:

不能以【返回值类型】来决定是否是重载

不能以【访问修饰符】来决定是否是重载

//方法重载的使用
static void Main(string[] args)
{
    Calc c1=new Calc();
    Console.WriteLine(c1.Pay(1, 9));  //输出:10
    Console.WriteLine(c1.Pay(1.1, 5.9));  //输出:7
    Console.WriteLine(c1.Pay("你好,", "世界"));  //输出:你好,世界
    Console.ReadLine();
} 
class Calc
{
   public Calc(){}
    public int Pay(int a,int b)
    {
        int sum =a+b;
        return sum;
    }
    public double Pay(double a,double b)
    {
        double sum =a+b;
        return sum;
    }
    public string Pay(string a,string b)
    {
        string sum =a+b;
        return sum;
    }
}

第六章 初识继承和多态

一、继承

1.概念:描述两个类之间的关系,子类是继承的类,子类又叫派生类,父类是被继承的类,父类又叫基类或者超类。

2.语法:访问修饰符 class 子类:父类

3.访问修饰符:

public:公共的、公开的

protected:受保护的,被这个访问修饰符修饰的成员允许被其子类访问,而不允许其他非子类访问。

private:私有的,只能在类的内部使用。

注意:一旦产生继承关系,子类可以访问所有父类非私有成员。

4.构造函数

所有子类的构造函数都会调用父类的构造函数,默认情况下调用父类的无参构造函数,也可以显示的调用父类的构造函数。使用base关键字:

在子类的构造函数括号外使用:base()则表示显示调用父类的无参构造函数。

:base(值)则表示显示调用父类的有参构造函数。

注意:使用base调用父类的有参构造函数时,()里面的值是实参,而不是形参!

public class Person
{
    public string Name{get;set;}
    public int Age{get;set;}
    //构造函数
    public Person(){}
    public Person(string name,int age)
    {
        this.Name=name;
        this.Age=age;
    }
}

//子类
public class Student:Person
{
	public string Hobby{get;set;}
	public Student():base(){}   //显示调用父类无参构造函数,没什么意义:因为默认情况下子类会调用父类的无参构造函数
	public Student(string name,int age,string hobby):base(name,age)//显示调用父类的有参构造函数
	{
		this.Name=name;
		this.Age=age;
		this.Hobby=hobby;
	}
}

注意:一旦手写了构造函数,那么这个类就不存在有隐藏的无参构造函数。所以当子类调用父类的构造函数时,一定要看清楚父类的构造函数!

5.base和this的区别

this是指当前类的成员的引用;base是指在当前类中调用父类的成员。

6.继承的特性:

①单根性:每一个类都只能有一个父类;

②传递性:孙子可以拥爷爷辈的基因

注意:每一个类如有没有显示定义父类,都有一个默认的父类:Object;如果某一个类想禁止被继承,在class前面使用关键字:sealed(密封类)

7.is和as

is a是判断对象是否属于给定的数据类型,返回布尔类型,如果属于返回true,否则返回false。

语法:引用名[对象名] is 数据类型;

as a是用来强制转换

语法:数据类型 变量名 = 对象名 as 数据类型;

等价于 数据类型 变量 = (数据类型)对象名;

public class Employee
{
    public void SayHi()
    {
        Console.WriteLine("大家好!");
    }
}

public class SE:Employee
{
	Console.WriteLine("大家好!我是员工");
}
public class PM:Employee
{
	Console.WriteLine("大家好!我是经理");
}

static void Main(string [] args)
{
    List<Employee> empls =new List<Employee>();
    //省略初始化代码
    foreach(Employee emp empls)
    {
        if(emp is SE)
        {
            SE se =emp as SE;
            Console.WriteLine(se.SayHi());
        }
        if(emp is PM)
        {
            PM pm =emp as PM;
             Console.WriteLine(pm.SayHi());
        }
    }
}

二、多态

1.概念:在同一范畴中,一个事物的多种形态。

2.实现多态的方式:重载和重写。

3.重写:
1.前提:继承关系

​ 2.特点:在不同的类中,相同的方法名和相同的参数,返回值类型也相同,但是方法体不同。

​ 3.标注方式:①虚方法;②抽象方法

4.虚方法:

①实现方式:父类的方法用关键字virtual修饰,子类将重写的方法使用override关键字。

②注意:父类的方法必须有方法体

public class Person
{
    public Person(){}
    public virtual void SayHi()
    {
        Console.WriteLine("大家好!");
    }
}

//子类
public class Student:Person
{
	public int ID{get;set;}  //学号
    public Student(){}
    public override void SayHi()
    {
    	Console.WriteLine("大家好!我的学号是{0}",this.ID);
    }
}

第七章 深入理解多态

一、里氏替换原则

1.里氏替换原则的概念:所有引用父类的地方,都可以透明的使用子类对象。

2.继承的优点:①提供了代码的重用性,子类拥有父类的属性和方法;

②提供了代码的可扩展性,子类可以形似父类,也异于父类保留自我个性。

3.继承的缺点:①继承侵入式编程【子类拥有父类的属性和方法】

只要有继承关系,父类里面所有动作子类也一定有,实际上父类约束了子类的行动。

②增加了耦合性

二、抽象类和抽象方法

1.抽象类:

①语法:访问修饰符 abstract class 类名{}

②抽象类的特点:不能实例化对象,抽象类不能使用sealed和static修饰,抽象类中可以有抽象方法也可以有非抽象的方法。

2.抽象方法:

①定义:访问修饰符 abstract 返回值类型 方法名(参数列表){}

②特点:在不同的类中,方法名相同和参数相同,返回值类型也相同,抽象方法没有方法体,并且只能位于抽象类中,父类里面的抽象方法子类必须实现,除非子类也是抽象的。

③实现方式:前提在抽象中,父类的方法用关键字abstract修饰,子类被重写的方法使用关键字override修饰。

3.存在的意义:规定子类必须重写父类的方法。

//父类:动物类
public abstract  class Animal
{
    //抽象方法
    public abstract void Eat();
}

public class Monkey:Animal
{
	//子类必须重写父类的方法
	 public override void Eat()
     {
     	Console.WriteLine("猴子吃香蕉!");
     }
}

三、虚方法和抽象方法的区别

1.相同点:都是在约定子类拥有某个动作

虚方法:只能约定子类的动作可以是父类实现的。

抽象方法:只能子类自己实现,因为抽象方法没有方法体。

2.不同点:

不同点 定义关键字不同 方法体的要求 对类要求的不一样(密封类除外) 对子类的实现不一样
虚方法 virtual 必须有方法体 可以位于任意类 不强制要求子类必须实现
抽象方法 abstract 不能有方法体 只能位于抽象类 子类必须实现,除非子类也是抽象

四、软件设计原则

1.里氏替换原则:父类的引用指向子类的对象

2.单一职责原则:一个类只做一件事

3.依赖倒置原则:细节依赖抽象,而不是抽象依赖于细节

4.接口隔离原则:接口:只有抽象方法,

5.开闭原则:对修改关闭,对扩展开放

发布了14 篇原创文章 · 获赞 4 · 访问量 772

猜你喜欢

转载自blog.csdn.net/WW0724/article/details/104291058
今日推荐