什么是面向对象面向对象就是把现实中的事物都抽象为“对象”。每个对象是唯一的且都可以拥有它的属性与行为。我们就可以通过调用这些对象的方法、属性去解决问题。什么是对象对象是面向对象编程中的一个概念它是类的一个实例化即具体化的结果。对象是具体的、实际存在的可以在程序中被创建、操作和销毁。什么是类?每个对象都属于某个类类是对象的抽象它定义了对象所具有的属性和方法的结构和行为。对象通过实例化类来创建并且可以根据类的定义进行属性和方法的访问和调用。为什么非要创建对象才能使用呢类只是一种程序内的“设计图纸”或者摸具需要基于图纸或摸具生产实体对象才能正常工作这种套路称之为面向对象编程类就相当于是闹钟的设计图纸而对象就相当于按照闹钟的设计图纸所生产出来的闹钟。一、类和对象的创建封装1对象的创建类名 对象名 new 类名对象名.成员名称值new的时候{成员名称值}2获取成员的值对象名.成员名称3方法(对象的行为)调用对象名.方法名()internal class Program { static void Main(string[] args) { //类只是提供一个类型,凡是由类构建出来对象都属于同一种类型 //对象创建 需要使用构造函数进行创建 People zs new People(); zs.id 002;//访问公共的属性 People ls new People(); ls.id 001; //调用成员方法 //调用非静态的方法 (对象方法) zs.F1(); ls.F1(); //调用静态的方法(类方法) 类名去调用 People.F2(10); } } //类的创建位置:在namespace范围内. //创建语法: //修饰符:public 公共的 ,private私有的 protected受保护的,internal内部访问,类一般使用public 或者internal //class 关键字 //类名:大驼峰命名法则 //类里面包含成员变量:字段 属性 方法 public class People { //字段又叫成员变量 //定义成员变量的语法 //修饰符 类型 变量名; string name;//字段 int age;//字段 bool sex;//字段 默认情况下字段是私有的 public string id;//字段 private int _height;// //成员方法: 修饰符 static/非static void/数据类型 方法名(参数列表){} public void F1() { Console.WriteLine(非静态无返回值方法); } public static int F2(int a) { return 100000; } }二、字段和属性1.字段和属性区别字段和属性区别1用途不同字段是内部使用属性是外部使用。2权限不同字段一般是私有的但属性一般是公开的。3结构不同字段是存数据的变量属性带有get/set访问器本身不存数据只是对字段的访问包装。4安全性不同字段直接暴露数据不安全、不可控属性包装字段可以加验证、逻辑安全、可控。1私有字段_开头 小驼峰private int _age;2公开属性大驼峰public int Age { get; set; }3赋值 自动跑 set4读取 自动跑 get5set 里可以写逻辑、调用方法等赋值时自动执行1同个类内方法中直接写属性名就能访问当前对象的属性编译器会默认加上this.2this代表当前实例对象this.属性名this可以省略。什么时候必须写 this. 当局部变量 / 参数 和 属性重名时必须用this.区分否则编译器会优先找局部变量无命名冲突属性名/this.属性名都可以随意写有命名冲突必须加this.区分属性和局部变量 / 参数internal class Program { static void Main(string[] args) { //创建对象 People p new People(); p.Name T; //在给属性赋值的时候触发set访问器 Console.WriteLine(p.Name);//取值的话触发get访问器 People p3 new People(); p3.Name Y; // p3.Msg S; //只能读取 不能修改 Console.WriteLine(p3.Name:p3.Id); } } public class People { private int _age;// 私有的字段 只能在内部访问 //最早的属性定义方式 //先添加一个私有的字段 //再添加一个公共的属性,可以添加get set访问器 private string _name;//字段 public string Name //属性 { get{ Console.WriteLine(get访问器触发了); return _name; } //在外部获取属性所关联字段值 触发get访问器 ,需要添加return 关联字段 set{ Console.WriteLine(set访问器触发了value); if (value.Length0) { throw new Exception(不能赋空字符串); } _name value; } //在给属性赋值的时候触发set访问器 把一个value赋值给字段, } //2 优化上面属性的写法 private int _a1; public int A1 { get _a1; set _a1 value; } //3如果不想加属性拦截 get set不想写任何东西 //属性语法糖写法 public string Id { get; set; } 001; // 可以保留一个get 使属性变成只读的属性 public string Msg { get; } }2.属性拦截例子namespace _8属性拦截例子 { internal class Program { static void Main(string[] args) { Dog d1 new Dog(); d1.Age 19; Console.WriteLine(d1.Age); d1.Color 白; Console.WriteLine(d1.Color); } } }namespace _8属性拦截例子 { internal class Dog { public int a100; private int _age; public int Age { get { _age * 10; return _age; } //get访问器 对象.属性 set { if (value 0||value20) { throw new Exception(年龄不在合适的范围内);// } _age value; //年龄在0-20之间 }//set访问 对象.属性10 } private char[] colors { 黄, 白, 黑 }; private char _color; public char Color { get { return _color; } set { if (!colors.Contains(value)) { throw new Exception(颜色不合适); } _color value; } } } }三、构造函数在 C# 中构造函数的名称必须与类名一致并且它不能具有返回类型如void或int。它在类中的地位比较特殊不需要我们主动调用当创建一个类的对象时会自动调用类中的构造函数。默认地所有类都有构造函数如果你不自己创建类构造函数C# 会为你创建一个。但是那么你将无法为字段设置初始值。注意: 类中默认有一个无参构造函数,但是如果手动添加了有参构造函数后,就会把无参的构造函数覆盖掉namespace _9构造函数 { internal class Program { static void Main(string[] args) { //构造函数:People() 和类的名称是一样的.作用创建对象和初始化属性的值, 可以有非静态的构造函数,也可以有静态构造函数 //创建一个类默认提供一个不带参数构造函数,也可以定义有参数的构造函数. People p1 new People(); Console.WriteLine(p1.Name);//张三 People p2 new People(); Console.WriteLine(p2.Name);//张三 People p3 new People(李四,20); Console.WriteLine( p3.Name); People p4 new People(王五, 20); Console.WriteLine(p4.Name); } } public class People { public string Name { get; set; } public int Age { get; set; } //不带参数的构造函数 public People() { Name 张三; this.Age 10; // this当前类的对象,不是具体的对象 , } public People(string n, int a) { this.Name n; //把参数赋值给属性 this.Age a; Console.WriteLine(this.Name ---------); } } }四、析构函数1析构函数是一个特殊的成员函数当一个对象被释放的时候执行2被释放C#有垃圾回收机制(GC) 当一个数据没有被任何变量所引用的时候垃圾回收机制就会把这个对象当作垃圾回收掉3 析构函数当类的某个对象被当作垃圾回收的时候这个函数就会自动调用4析构函数的作用是释放对象的资源但析构函数是由垃圾回收器控制的无法做到显式调用所以使用析构函数不是最佳方案(很少使用)namespace _10析构函数 { internal class Program { static void Main(string[] args) { People p new People(); p null; GC.Collect();//GC强制回收,对象不再被引用的时候 进行回收 Console.ReadKey(); } } public class People { //当一个对象不再被其他变量引用的时候,可以通过GC垃圾回收机制强制回收,这个时候会触发析构函数 ~People() { Console.WriteLine(对象被GC回收了); } } }五、静态成员属性、方法1.静态与非静态区别1归属不同静态成员属于类非静态成员属于实例对象。2调用方式不同静态成员通过类名.成员调用无需实例化非静态成员必须先new创建对象再通过对象名.成员调用。3内存特点不同程序运行时只分配一次内存全局唯一所有对象共享同一份数据非静态成员每创建一个对象就单独分配一份内存。4访问规则不同静态方法只能使用静态的属性和静态方法若要访问实例成员必须先实例化对象非静态方法不仅能使用静态属性、静态方法还能直接使用实例属性、实例方法。简洁版静态属于类非静态属于对象静态不用 new非静态必须 new 静态全局只有一份非静态每个对象独立一份静态只能使用静态非静态全都能用为什么是这样静态属于类程序一运行就存在非静态属于对象必须new才存在静态方法运行时对象还没创建呢所以根本用不了非静态什么时候用静态工具方法、共用数据、全局唯一非静态每个对象不一样的属性 / 方法姓名、年龄、成绩等internal class Program { static void Main(string[] args) { //非静态找对象去调用 People p1 new People(); p1.F1(); p1.Id 1; //静态的方法 类名.属性/方法 People.Age 10; People.F2(); } }//静态属性定义在类空间里面,每一个实例都拥有同一个静态数据 //非静态属性 每个实例拥有对立一份数据 public class People { //非静态成员变量和非静态成员方法 private int id; public int Id { get { return id; } set { Console.WriteLine(ssssssssss); id value; //Id value; // Id是属性 属性10执行set访问器 切记不要写属性赋值,造成死循环 } } public void F1() { Console.WriteLine(非staticF1); //非静态的方法可以使用静态属性和静态方法 // Age 10; // F2(); } //静态成员变量和静态成员方法 public static int Age { get; set; } public static void F2() { //如果直接调用非静态的方法 不允许的,但是如果使用new 创建一个新的 ,这个新的对象可以调用 //People p new People(); //p.F1(); //p.Id 1; //静态方法只能使用静态的属性和静态方法 Console.WriteLine(StaticF2); } }2.静态方法测试internal class Program { static void Main(string[] args) { People p1 new People(); p1.AddNum(); People p2 new People(); p2.AddNum(); //Num属性的静态 每个实例都拥有同一个数据 //Num属性的非静态 每个实例都有一个独立的一份数据 Console.WriteLine(p1.GetNum());// 如果静态num值是2,非静态num值1 Console.WriteLine(p2.GetNum());// 如果静态num值是2,非静态num值1 } } public class People { public int Age { get; set; } public int Num { get; set; } public void AddNum() { Num; } public int GetNum() { return Num; } }六、常量和只读变量readonly 表示只读的一个字段只能在构造函数里面进行修改public static readonly静态的只读变量只能在静态构造函数里面进行修改静态构造函数只能被调用一次1.new的时候调用2.访问静态属性时候调用const 给字段加const 关键字 表示这个字段为常量不能修改internal class Program { static void Main(string[] args) { People p1 new People(); //p1.a 30; //不能其他进行修改 // Console.WriteLine(p1.a);//可以访问 Console.WriteLine(People.b); Console.WriteLine(People.c); } } public class People { //readonly 表示只读的一个字段,只能在构造函数里面进行修改, public readonly int a 10; public static readonly int b 20;//静态的只读变量 只能在静态构造函数;里面进行修改 public const int c 30;//常量,自动编译成一个静态的常量 public People() { // b 30; 静态只读不能在非静态的构造函数进行修改 a 20; Console.WriteLine(a);//20 } // 静态的构造函数不能使用修饰符 //静态构造函数只能被调用一次 1:new的时候调用了,2访问静态属性时候调用 static People() { b 40; Console.WriteLine(b----------------); Console.WriteLine(静态构造函数只能被调用一次 1:new的时候调用了,2访问静态属性时候调用 ); } }七、继承作用继承主要是为了代码复用把公共的属性和方法放到父类子类直接使用不用重复写。叫法A 类继承 B 类B 叫父类 / 基类A 叫子类 / 派生类语法C# 用冒号:实现继承。单继承C# 是单继承可以A → B → C链式继承不可以A 同时继承 B 和 C默认继承如果不写冒号类默认继承 object。object是所有类的最终基类祖宗类。internal class Program { static void Main(string[] args) { Student s1 new Student(); s1.Name Test; s1.Address s; s1.F1(); People p1 new People(); } } //定义父类 public class People { public int Id { get; set; } public string Name { get; set; } public string Address { get; set; } private int Age 1000; //私有的不能被继承 protected int CC { get; set; }//受保护的属性 People的实例不能访问,子类可以使用 public void F1() { Console.WriteLine( 父类的F1方法); } } //学生类 继承于People public class Student:People { public void F2() { Console.WriteLine(Name:Id:CC); } }1.子类构造和父类构造在子类继承父类时创建子类对象不管无参 / 有参会先调用父类的无参构造函数再调用子类自己的构造函数如果想主动调用父类的有参构造必须在子类构造函数后面加: base (参数)一旦加了: base (参数)父类无参构造就不会再执行了。父类的构造函数一次只会执行一个要么执行无参要么执行有参不可能两个都执行internal class Program { static void Main(string[] args) { // People p1 new People(); // People p2 new People(福瑞); //在子类继承父类的时候,创建子类的对象无参或有参,会先调用父类的无参构造函数,在调用子类自己 // 的构造函数。 // Student s1 new Student(); //1 父类的无参数构造函数 //3子类的无参数构造函数 //即使子类调用带参数构造函数,默认情况下还是先调用父类的无参数构造函数 //如果想调用父类有参构造函数 在子类有参数构造函数后面添加base(11)调用父类有参数构造 。 Student s2 new Student(10,张三); //2 父类带参数的构造函数 //4子类带参数的构造函数 } } public class People { public string Name { get; set; } public People() { Console.WriteLine(1 父类的无参数构造函数); } public People(string name) { Console.WriteLine(2 父类带参数的构造函数); } } public class Student:People { public int Age { get; set; } public Student():base() //base指的是父类的对象, :base()调用父;类的构造函数 { Console.WriteLine(3子类的无参数构造函数); } public Student(int age, string n) : base(n) //:base(n) 调用父类的有参数构造函数 { Console.WriteLine(4子类带参数的构造函数Name); } }2.访问修饰符访问修饰符/范围当前类子类实例对象引用当前项目的项目子类引用当前项目实例对象public√√√√√private√××××internal√√√××protected√√×√×protected internal√√√√×八、多态多态的分类编译时多态静态多态通过方法重载Method Overloading和运算符重载Operator Overloading来实现。运行时多态动态多态通过继承和接口实现以及方法重写Method Overriding来实现。静态多态在编译过程中,通过方法重载和运算符重载来实现,也称之为静态绑定和早期绑定动态多态在运行过程中,通过方法重写,隐藏方法来实现,也称之为动态绑定或者后期绑定一、函数重载函数重载满足条件1函数名必须一样2参数的个数 或者 类型不一样3仅仅只有返回值类型不一样不是重。internal class Program { static void Main(string[] args) { Console.WriteLine(); } public static void F1() { Console.WriteLine(F1不带参数); } public static void F1(int a) { Console.WriteLine(F1带一个参数); } public static void F1(string a) { Console.WriteLine(F1带一个参数); } public static void F1(string a,int b) { Console.WriteLine(F1带俩个参数); } public static void F1(int b,string a) { Console.WriteLine(F1带俩个参数); } public static int F1(bool b) { Console.WriteLine(F1带1个参数); return 10; } }二、运算符重载符号重载对运算符号或者比较符号等进行功能的重写 例如可以让俩个对象能够实现相加核心规则必须用public static必须用operator关键字必须返回值不能是 void二元运算符、-、*、/必须带两个参数固定写法public static 返回值 operator 运算符参数列表运算符类别可重载运算符关键备注算术运算符、-、*、/、%二元运算符必须带 2 个参数一元算术运算符、--、、-、~一元运算符仅需 1 个参数比较运算符、!、、、、必须成对重载如重载必须同时重载!位运算符、、^、、支持按位运算自定义逻辑特殊运算符true、false用于自定义类型的布尔判断运算符类别不能重载的运算符赋值运算符逻辑与逻辑或||三元运算符?:类型判断is类型转换as获取大小sizeof获取类型typeofinternal class Program { static void Main(string[] args) { //符号重载:对运算符号或者比较符号等进行功能的重写 例如可以让俩个对象能够实现相加. Box b1 new Box(10,20,30); Box b2 new Box(30,40,50); Box b3 b1 b2; Console.WriteLine(b3.Length:b3.Width:b3.Height); int b4 b1 -b2; Console.WriteLine(b4); Box b5 b1; Console.WriteLine(b1.Length);//11 Console.WriteLine(b5.Length);//10 Console.WriteLine(b1b2);//false //执行符号 执行重载函数,重载函数比较了b1和b2的体积, b1.vb2.v成立,重载结果就为true Console.WriteLine( b1b2); //true } }public class Box { public int Length { get; set; } //长 public int Width { get; set; } // 宽 public int Height { get; set; }//高 public Box() { } //体积的属性 public int Volume { get { return Length*Width*Height; } } public Box(int l, int w,int h) { Length l; Width w; Height h; } //重载加 实现俩个对象相加 返回一个对象,结果对象三个属性分别是俩个对象属性相加和 public static Box operator(Box b1,Box b2) { //return b1.Length*b1.Width*b1.Height b2.Length * b2.Width * b2.Height; return new Box() { Length b1.Length b2.Length, Width b1.Width b2.Width, Height b1.Height b2.Height }; } //重载- ,俩个对象相减等到俩个体积相减的差值 public static int operator-(Box b1,Box b2) { return b1.Length * b1.Width * b1.Height - b2.Length * b2.Width * b2.Height; } //重载 让对象的长宽高在基础之上加一 并且返回一个对象 public static Box operator(Box b1) { return new Box() { Length b1.Length 1, Width b1.Width 1, Height b1.Height 1, }; } //重载大于号 public static bool operator(Box b1,Box b2) { return b1.Volume b2.Volume; } //重载小于符号 public static bool operator (Box b1, Box b2) { return b1.Volume b2.Volume; } }三、抽象类abstract抽象方法和普通方法区别1抽象方法只能定义在抽象类里面普通方法可以定义在抽象类也可以定义普通类中2抽象方法没有方法体普通方法需要有方法体3抽象方法 / 属性必须在子类重写override实现普通方法则不必须。抽象类和普通类区别1抽象类必须加abstract普通类不需要关键字2抽象类不能 new 对象普通类可以直接 new 对象2抽象类可以有抽象方法 抽象属性普通类绝对不能有任何抽象成员3抽象类普通子类继承后必须重写override所有抽象成员 普通类子类继承后不需要重写任何东西直接用5抽象类普通属性、普通方法、抽象属性、抽象方法普通类只能写普通属性、普通方法。internal class Program { static void Main(string[] args) { //抽象类: //定义时候使用abstract关键字,不能实例化 //抽象类存在的意义就是为了被继承的, 所以一般是公共的类 //可以定义普通属性和方法 也可以定义抽象属性和抽象方法,需要在子类把抽象的属性和方法进行实现 //抽象类可以继承自抽象类,子类如果不是抽象类,就必须重写抽象类中全部的抽象方法 // 抽象方法和普通方法区别 // 抽象方法只能定义在抽象类里面,普通方法可以定义在抽象类 也可以定义普通的类中 // 抽象方法没有方法体,普通方法需要有方法体 // 抽象方法在子类实现. 需要通过override重新抽象方法 } } //定义抽象类 abstract class People { public int Age { get; set; }//普通的属性 public void F2() // 普通方法 { Console.WriteLine(F2非抽象方法); } public abstract string Name { get; set; } // 定义抽象属性 public abstract void F1(); //抽象方法 不要加{} } //定义子类继承抽象类 必须实现抽象属性和抽象方法 class Student : People { public override string Name { get; set; } ssss; public override void F1() { Console.WriteLine(子类的F1 ); } }四、虚方法virtual抽象方法与虚方法区别1抽象方法abstract虚方法virtual;2抽象方法只能定义在抽象类中虚方法普通类、抽象类都可以定义;3抽象方法没方法体子类必须重写虚方法有方法体子类可选重写。4虚方法和new 或者override进行联合使用internal class Program { static void Main(string[] args) { China c new China(); c.SayHellow(); } }public class People { public string Name { get; set; } public virtual void SayHellow() { Console.WriteLine(People打招呼); } } public class China :People { //父类的虚方法 需要在子类通过override进行重写,覆盖父类的方法 // 也可以不用重写,调用的父类的方法 public override void SayHellow() { Console.WriteLine(吃了吗,抽个烟); } } public class Japan : People { //父类的虚方法 需要在子类通过override进行重写,覆盖父类的方法 // 也可以不用重写,调用的父类的方法 public override void SayHellow() { Console.WriteLine(汪汪); } } public class HanGuo : People { //父类的虚方法 需要在子类通过override进行重写,覆盖父类的方法 // 也可以不用重写,调用的父类的方法 public override void SayHellow() { Console.WriteLine(啊你赛有); } }五、new 和 override在重写虚方法的时候 可以使用new 或者overridenew 或者override 区别new创建一个新的方法父类和子类都保留了一份他们是相互独立调用new重写方法时候就看变量的类型变量类型如果是父类调用父类的方法如果是子类调用子类自己的方法。override相当于把父类的方法覆盖掉了不管变量类型是子类类型还是父类类型调用的是子类的方法1. new隐藏 / 独立父类、子类各存一份方法互不干扰调用谁看变量类型变量是父类 → 调父类 变量是子类 → 调子类2. override重写 / 覆盖父类方法被覆盖调用谁看真实对象是谁不管变量怎么写永远调用最终子类的方法internal class Program { static void Main(string[] args) { // 创建的对象如果使用自己类型的变量去接收, 调用的时候调用自己类的方法 //People p new People(); // p.Test1(); // p.Test2(); // p.Test3(); People p1 new Student(); // 找父类类型的变量 接收子类的对象 Student s1 new Student(); //找自己类的类型变量接收对象 p1.Test1();//People的Test1 s1.Test1();//Student的Test1 p1.Test2();//People的Test2 s1.Test2();//Student的Test2 p1.Test3();//Student的Test3 s1.Test3();//Student的Test3 } }public class People { public virtual void Test1() { Console.WriteLine(People的Test1); } public virtual void Test2() { Console.WriteLine(People的Test2); } public virtual void Test3() { Console.WriteLine(People的Test3); } } public class Student : People { public new void Test1() { Console.WriteLine(Student的Test1); } public new void Test2() { Console.WriteLine(Student的Test2); } public override void Test3() { Console.WriteLine(Student的Test3); } }六、密封类和静态类internal class Program { static void Main(string[] args) { //Test t1 new Test(); // Test1 t2 new Test1(); //Test2 t3 new Test2(); } } //抽象类型 不能实例化,用来被继承的 abstract class Test { } //密封类 不能被继承 能够被实例化 sealed class Test1 { } //静态类要求成员都是静态的, 不能被实例化 static class Test2 { public static int Age { get; set; } } //部分类:把一个分成多个类文件进行定义,编译器在编译时候会把多个文件自动合并一个类. // C# 部分类partial class 是 C# 中的部分类核心作用是将一个类的代码拆分到多个文件中编译时编译器会自动把这些文件合并成一个完整的类。 //它的关键字是 partial必须和 class 配合使用。 // 每个文件中的类都必须用 partial 修饰 // 类名、命名空间必须完全一致 // 最终编译后所有部分会合并为一个类 partial class Test3 { } }七、重载和重写区别特点重载 (Overload)重写 (Override)位置同一个类父子类继承方法名相同相同参数必须不同必须相同返回值无关必须相同关键字无virtualoverride
【c#基础】9.面向对象
什么是面向对象面向对象就是把现实中的事物都抽象为“对象”。每个对象是唯一的且都可以拥有它的属性与行为。我们就可以通过调用这些对象的方法、属性去解决问题。什么是对象对象是面向对象编程中的一个概念它是类的一个实例化即具体化的结果。对象是具体的、实际存在的可以在程序中被创建、操作和销毁。什么是类?每个对象都属于某个类类是对象的抽象它定义了对象所具有的属性和方法的结构和行为。对象通过实例化类来创建并且可以根据类的定义进行属性和方法的访问和调用。为什么非要创建对象才能使用呢类只是一种程序内的“设计图纸”或者摸具需要基于图纸或摸具生产实体对象才能正常工作这种套路称之为面向对象编程类就相当于是闹钟的设计图纸而对象就相当于按照闹钟的设计图纸所生产出来的闹钟。一、类和对象的创建封装1对象的创建类名 对象名 new 类名对象名.成员名称值new的时候{成员名称值}2获取成员的值对象名.成员名称3方法(对象的行为)调用对象名.方法名()internal class Program { static void Main(string[] args) { //类只是提供一个类型,凡是由类构建出来对象都属于同一种类型 //对象创建 需要使用构造函数进行创建 People zs new People(); zs.id 002;//访问公共的属性 People ls new People(); ls.id 001; //调用成员方法 //调用非静态的方法 (对象方法) zs.F1(); ls.F1(); //调用静态的方法(类方法) 类名去调用 People.F2(10); } } //类的创建位置:在namespace范围内. //创建语法: //修饰符:public 公共的 ,private私有的 protected受保护的,internal内部访问,类一般使用public 或者internal //class 关键字 //类名:大驼峰命名法则 //类里面包含成员变量:字段 属性 方法 public class People { //字段又叫成员变量 //定义成员变量的语法 //修饰符 类型 变量名; string name;//字段 int age;//字段 bool sex;//字段 默认情况下字段是私有的 public string id;//字段 private int _height;// //成员方法: 修饰符 static/非static void/数据类型 方法名(参数列表){} public void F1() { Console.WriteLine(非静态无返回值方法); } public static int F2(int a) { return 100000; } }二、字段和属性1.字段和属性区别字段和属性区别1用途不同字段是内部使用属性是外部使用。2权限不同字段一般是私有的但属性一般是公开的。3结构不同字段是存数据的变量属性带有get/set访问器本身不存数据只是对字段的访问包装。4安全性不同字段直接暴露数据不安全、不可控属性包装字段可以加验证、逻辑安全、可控。1私有字段_开头 小驼峰private int _age;2公开属性大驼峰public int Age { get; set; }3赋值 自动跑 set4读取 自动跑 get5set 里可以写逻辑、调用方法等赋值时自动执行1同个类内方法中直接写属性名就能访问当前对象的属性编译器会默认加上this.2this代表当前实例对象this.属性名this可以省略。什么时候必须写 this. 当局部变量 / 参数 和 属性重名时必须用this.区分否则编译器会优先找局部变量无命名冲突属性名/this.属性名都可以随意写有命名冲突必须加this.区分属性和局部变量 / 参数internal class Program { static void Main(string[] args) { //创建对象 People p new People(); p.Name T; //在给属性赋值的时候触发set访问器 Console.WriteLine(p.Name);//取值的话触发get访问器 People p3 new People(); p3.Name Y; // p3.Msg S; //只能读取 不能修改 Console.WriteLine(p3.Name:p3.Id); } } public class People { private int _age;// 私有的字段 只能在内部访问 //最早的属性定义方式 //先添加一个私有的字段 //再添加一个公共的属性,可以添加get set访问器 private string _name;//字段 public string Name //属性 { get{ Console.WriteLine(get访问器触发了); return _name; } //在外部获取属性所关联字段值 触发get访问器 ,需要添加return 关联字段 set{ Console.WriteLine(set访问器触发了value); if (value.Length0) { throw new Exception(不能赋空字符串); } _name value; } //在给属性赋值的时候触发set访问器 把一个value赋值给字段, } //2 优化上面属性的写法 private int _a1; public int A1 { get _a1; set _a1 value; } //3如果不想加属性拦截 get set不想写任何东西 //属性语法糖写法 public string Id { get; set; } 001; // 可以保留一个get 使属性变成只读的属性 public string Msg { get; } }2.属性拦截例子namespace _8属性拦截例子 { internal class Program { static void Main(string[] args) { Dog d1 new Dog(); d1.Age 19; Console.WriteLine(d1.Age); d1.Color 白; Console.WriteLine(d1.Color); } } }namespace _8属性拦截例子 { internal class Dog { public int a100; private int _age; public int Age { get { _age * 10; return _age; } //get访问器 对象.属性 set { if (value 0||value20) { throw new Exception(年龄不在合适的范围内);// } _age value; //年龄在0-20之间 }//set访问 对象.属性10 } private char[] colors { 黄, 白, 黑 }; private char _color; public char Color { get { return _color; } set { if (!colors.Contains(value)) { throw new Exception(颜色不合适); } _color value; } } } }三、构造函数在 C# 中构造函数的名称必须与类名一致并且它不能具有返回类型如void或int。它在类中的地位比较特殊不需要我们主动调用当创建一个类的对象时会自动调用类中的构造函数。默认地所有类都有构造函数如果你不自己创建类构造函数C# 会为你创建一个。但是那么你将无法为字段设置初始值。注意: 类中默认有一个无参构造函数,但是如果手动添加了有参构造函数后,就会把无参的构造函数覆盖掉namespace _9构造函数 { internal class Program { static void Main(string[] args) { //构造函数:People() 和类的名称是一样的.作用创建对象和初始化属性的值, 可以有非静态的构造函数,也可以有静态构造函数 //创建一个类默认提供一个不带参数构造函数,也可以定义有参数的构造函数. People p1 new People(); Console.WriteLine(p1.Name);//张三 People p2 new People(); Console.WriteLine(p2.Name);//张三 People p3 new People(李四,20); Console.WriteLine( p3.Name); People p4 new People(王五, 20); Console.WriteLine(p4.Name); } } public class People { public string Name { get; set; } public int Age { get; set; } //不带参数的构造函数 public People() { Name 张三; this.Age 10; // this当前类的对象,不是具体的对象 , } public People(string n, int a) { this.Name n; //把参数赋值给属性 this.Age a; Console.WriteLine(this.Name ---------); } } }四、析构函数1析构函数是一个特殊的成员函数当一个对象被释放的时候执行2被释放C#有垃圾回收机制(GC) 当一个数据没有被任何变量所引用的时候垃圾回收机制就会把这个对象当作垃圾回收掉3 析构函数当类的某个对象被当作垃圾回收的时候这个函数就会自动调用4析构函数的作用是释放对象的资源但析构函数是由垃圾回收器控制的无法做到显式调用所以使用析构函数不是最佳方案(很少使用)namespace _10析构函数 { internal class Program { static void Main(string[] args) { People p new People(); p null; GC.Collect();//GC强制回收,对象不再被引用的时候 进行回收 Console.ReadKey(); } } public class People { //当一个对象不再被其他变量引用的时候,可以通过GC垃圾回收机制强制回收,这个时候会触发析构函数 ~People() { Console.WriteLine(对象被GC回收了); } } }五、静态成员属性、方法1.静态与非静态区别1归属不同静态成员属于类非静态成员属于实例对象。2调用方式不同静态成员通过类名.成员调用无需实例化非静态成员必须先new创建对象再通过对象名.成员调用。3内存特点不同程序运行时只分配一次内存全局唯一所有对象共享同一份数据非静态成员每创建一个对象就单独分配一份内存。4访问规则不同静态方法只能使用静态的属性和静态方法若要访问实例成员必须先实例化对象非静态方法不仅能使用静态属性、静态方法还能直接使用实例属性、实例方法。简洁版静态属于类非静态属于对象静态不用 new非静态必须 new 静态全局只有一份非静态每个对象独立一份静态只能使用静态非静态全都能用为什么是这样静态属于类程序一运行就存在非静态属于对象必须new才存在静态方法运行时对象还没创建呢所以根本用不了非静态什么时候用静态工具方法、共用数据、全局唯一非静态每个对象不一样的属性 / 方法姓名、年龄、成绩等internal class Program { static void Main(string[] args) { //非静态找对象去调用 People p1 new People(); p1.F1(); p1.Id 1; //静态的方法 类名.属性/方法 People.Age 10; People.F2(); } }//静态属性定义在类空间里面,每一个实例都拥有同一个静态数据 //非静态属性 每个实例拥有对立一份数据 public class People { //非静态成员变量和非静态成员方法 private int id; public int Id { get { return id; } set { Console.WriteLine(ssssssssss); id value; //Id value; // Id是属性 属性10执行set访问器 切记不要写属性赋值,造成死循环 } } public void F1() { Console.WriteLine(非staticF1); //非静态的方法可以使用静态属性和静态方法 // Age 10; // F2(); } //静态成员变量和静态成员方法 public static int Age { get; set; } public static void F2() { //如果直接调用非静态的方法 不允许的,但是如果使用new 创建一个新的 ,这个新的对象可以调用 //People p new People(); //p.F1(); //p.Id 1; //静态方法只能使用静态的属性和静态方法 Console.WriteLine(StaticF2); } }2.静态方法测试internal class Program { static void Main(string[] args) { People p1 new People(); p1.AddNum(); People p2 new People(); p2.AddNum(); //Num属性的静态 每个实例都拥有同一个数据 //Num属性的非静态 每个实例都有一个独立的一份数据 Console.WriteLine(p1.GetNum());// 如果静态num值是2,非静态num值1 Console.WriteLine(p2.GetNum());// 如果静态num值是2,非静态num值1 } } public class People { public int Age { get; set; } public int Num { get; set; } public void AddNum() { Num; } public int GetNum() { return Num; } }六、常量和只读变量readonly 表示只读的一个字段只能在构造函数里面进行修改public static readonly静态的只读变量只能在静态构造函数里面进行修改静态构造函数只能被调用一次1.new的时候调用2.访问静态属性时候调用const 给字段加const 关键字 表示这个字段为常量不能修改internal class Program { static void Main(string[] args) { People p1 new People(); //p1.a 30; //不能其他进行修改 // Console.WriteLine(p1.a);//可以访问 Console.WriteLine(People.b); Console.WriteLine(People.c); } } public class People { //readonly 表示只读的一个字段,只能在构造函数里面进行修改, public readonly int a 10; public static readonly int b 20;//静态的只读变量 只能在静态构造函数;里面进行修改 public const int c 30;//常量,自动编译成一个静态的常量 public People() { // b 30; 静态只读不能在非静态的构造函数进行修改 a 20; Console.WriteLine(a);//20 } // 静态的构造函数不能使用修饰符 //静态构造函数只能被调用一次 1:new的时候调用了,2访问静态属性时候调用 static People() { b 40; Console.WriteLine(b----------------); Console.WriteLine(静态构造函数只能被调用一次 1:new的时候调用了,2访问静态属性时候调用 ); } }七、继承作用继承主要是为了代码复用把公共的属性和方法放到父类子类直接使用不用重复写。叫法A 类继承 B 类B 叫父类 / 基类A 叫子类 / 派生类语法C# 用冒号:实现继承。单继承C# 是单继承可以A → B → C链式继承不可以A 同时继承 B 和 C默认继承如果不写冒号类默认继承 object。object是所有类的最终基类祖宗类。internal class Program { static void Main(string[] args) { Student s1 new Student(); s1.Name Test; s1.Address s; s1.F1(); People p1 new People(); } } //定义父类 public class People { public int Id { get; set; } public string Name { get; set; } public string Address { get; set; } private int Age 1000; //私有的不能被继承 protected int CC { get; set; }//受保护的属性 People的实例不能访问,子类可以使用 public void F1() { Console.WriteLine( 父类的F1方法); } } //学生类 继承于People public class Student:People { public void F2() { Console.WriteLine(Name:Id:CC); } }1.子类构造和父类构造在子类继承父类时创建子类对象不管无参 / 有参会先调用父类的无参构造函数再调用子类自己的构造函数如果想主动调用父类的有参构造必须在子类构造函数后面加: base (参数)一旦加了: base (参数)父类无参构造就不会再执行了。父类的构造函数一次只会执行一个要么执行无参要么执行有参不可能两个都执行internal class Program { static void Main(string[] args) { // People p1 new People(); // People p2 new People(福瑞); //在子类继承父类的时候,创建子类的对象无参或有参,会先调用父类的无参构造函数,在调用子类自己 // 的构造函数。 // Student s1 new Student(); //1 父类的无参数构造函数 //3子类的无参数构造函数 //即使子类调用带参数构造函数,默认情况下还是先调用父类的无参数构造函数 //如果想调用父类有参构造函数 在子类有参数构造函数后面添加base(11)调用父类有参数构造 。 Student s2 new Student(10,张三); //2 父类带参数的构造函数 //4子类带参数的构造函数 } } public class People { public string Name { get; set; } public People() { Console.WriteLine(1 父类的无参数构造函数); } public People(string name) { Console.WriteLine(2 父类带参数的构造函数); } } public class Student:People { public int Age { get; set; } public Student():base() //base指的是父类的对象, :base()调用父;类的构造函数 { Console.WriteLine(3子类的无参数构造函数); } public Student(int age, string n) : base(n) //:base(n) 调用父类的有参数构造函数 { Console.WriteLine(4子类带参数的构造函数Name); } }2.访问修饰符访问修饰符/范围当前类子类实例对象引用当前项目的项目子类引用当前项目实例对象public√√√√√private√××××internal√√√××protected√√×√×protected internal√√√√×八、多态多态的分类编译时多态静态多态通过方法重载Method Overloading和运算符重载Operator Overloading来实现。运行时多态动态多态通过继承和接口实现以及方法重写Method Overriding来实现。静态多态在编译过程中,通过方法重载和运算符重载来实现,也称之为静态绑定和早期绑定动态多态在运行过程中,通过方法重写,隐藏方法来实现,也称之为动态绑定或者后期绑定一、函数重载函数重载满足条件1函数名必须一样2参数的个数 或者 类型不一样3仅仅只有返回值类型不一样不是重。internal class Program { static void Main(string[] args) { Console.WriteLine(); } public static void F1() { Console.WriteLine(F1不带参数); } public static void F1(int a) { Console.WriteLine(F1带一个参数); } public static void F1(string a) { Console.WriteLine(F1带一个参数); } public static void F1(string a,int b) { Console.WriteLine(F1带俩个参数); } public static void F1(int b,string a) { Console.WriteLine(F1带俩个参数); } public static int F1(bool b) { Console.WriteLine(F1带1个参数); return 10; } }二、运算符重载符号重载对运算符号或者比较符号等进行功能的重写 例如可以让俩个对象能够实现相加核心规则必须用public static必须用operator关键字必须返回值不能是 void二元运算符、-、*、/必须带两个参数固定写法public static 返回值 operator 运算符参数列表运算符类别可重载运算符关键备注算术运算符、-、*、/、%二元运算符必须带 2 个参数一元算术运算符、--、、-、~一元运算符仅需 1 个参数比较运算符、!、、、、必须成对重载如重载必须同时重载!位运算符、、^、、支持按位运算自定义逻辑特殊运算符true、false用于自定义类型的布尔判断运算符类别不能重载的运算符赋值运算符逻辑与逻辑或||三元运算符?:类型判断is类型转换as获取大小sizeof获取类型typeofinternal class Program { static void Main(string[] args) { //符号重载:对运算符号或者比较符号等进行功能的重写 例如可以让俩个对象能够实现相加. Box b1 new Box(10,20,30); Box b2 new Box(30,40,50); Box b3 b1 b2; Console.WriteLine(b3.Length:b3.Width:b3.Height); int b4 b1 -b2; Console.WriteLine(b4); Box b5 b1; Console.WriteLine(b1.Length);//11 Console.WriteLine(b5.Length);//10 Console.WriteLine(b1b2);//false //执行符号 执行重载函数,重载函数比较了b1和b2的体积, b1.vb2.v成立,重载结果就为true Console.WriteLine( b1b2); //true } }public class Box { public int Length { get; set; } //长 public int Width { get; set; } // 宽 public int Height { get; set; }//高 public Box() { } //体积的属性 public int Volume { get { return Length*Width*Height; } } public Box(int l, int w,int h) { Length l; Width w; Height h; } //重载加 实现俩个对象相加 返回一个对象,结果对象三个属性分别是俩个对象属性相加和 public static Box operator(Box b1,Box b2) { //return b1.Length*b1.Width*b1.Height b2.Length * b2.Width * b2.Height; return new Box() { Length b1.Length b2.Length, Width b1.Width b2.Width, Height b1.Height b2.Height }; } //重载- ,俩个对象相减等到俩个体积相减的差值 public static int operator-(Box b1,Box b2) { return b1.Length * b1.Width * b1.Height - b2.Length * b2.Width * b2.Height; } //重载 让对象的长宽高在基础之上加一 并且返回一个对象 public static Box operator(Box b1) { return new Box() { Length b1.Length 1, Width b1.Width 1, Height b1.Height 1, }; } //重载大于号 public static bool operator(Box b1,Box b2) { return b1.Volume b2.Volume; } //重载小于符号 public static bool operator (Box b1, Box b2) { return b1.Volume b2.Volume; } }三、抽象类abstract抽象方法和普通方法区别1抽象方法只能定义在抽象类里面普通方法可以定义在抽象类也可以定义普通类中2抽象方法没有方法体普通方法需要有方法体3抽象方法 / 属性必须在子类重写override实现普通方法则不必须。抽象类和普通类区别1抽象类必须加abstract普通类不需要关键字2抽象类不能 new 对象普通类可以直接 new 对象2抽象类可以有抽象方法 抽象属性普通类绝对不能有任何抽象成员3抽象类普通子类继承后必须重写override所有抽象成员 普通类子类继承后不需要重写任何东西直接用5抽象类普通属性、普通方法、抽象属性、抽象方法普通类只能写普通属性、普通方法。internal class Program { static void Main(string[] args) { //抽象类: //定义时候使用abstract关键字,不能实例化 //抽象类存在的意义就是为了被继承的, 所以一般是公共的类 //可以定义普通属性和方法 也可以定义抽象属性和抽象方法,需要在子类把抽象的属性和方法进行实现 //抽象类可以继承自抽象类,子类如果不是抽象类,就必须重写抽象类中全部的抽象方法 // 抽象方法和普通方法区别 // 抽象方法只能定义在抽象类里面,普通方法可以定义在抽象类 也可以定义普通的类中 // 抽象方法没有方法体,普通方法需要有方法体 // 抽象方法在子类实现. 需要通过override重新抽象方法 } } //定义抽象类 abstract class People { public int Age { get; set; }//普通的属性 public void F2() // 普通方法 { Console.WriteLine(F2非抽象方法); } public abstract string Name { get; set; } // 定义抽象属性 public abstract void F1(); //抽象方法 不要加{} } //定义子类继承抽象类 必须实现抽象属性和抽象方法 class Student : People { public override string Name { get; set; } ssss; public override void F1() { Console.WriteLine(子类的F1 ); } }四、虚方法virtual抽象方法与虚方法区别1抽象方法abstract虚方法virtual;2抽象方法只能定义在抽象类中虚方法普通类、抽象类都可以定义;3抽象方法没方法体子类必须重写虚方法有方法体子类可选重写。4虚方法和new 或者override进行联合使用internal class Program { static void Main(string[] args) { China c new China(); c.SayHellow(); } }public class People { public string Name { get; set; } public virtual void SayHellow() { Console.WriteLine(People打招呼); } } public class China :People { //父类的虚方法 需要在子类通过override进行重写,覆盖父类的方法 // 也可以不用重写,调用的父类的方法 public override void SayHellow() { Console.WriteLine(吃了吗,抽个烟); } } public class Japan : People { //父类的虚方法 需要在子类通过override进行重写,覆盖父类的方法 // 也可以不用重写,调用的父类的方法 public override void SayHellow() { Console.WriteLine(汪汪); } } public class HanGuo : People { //父类的虚方法 需要在子类通过override进行重写,覆盖父类的方法 // 也可以不用重写,调用的父类的方法 public override void SayHellow() { Console.WriteLine(啊你赛有); } }五、new 和 override在重写虚方法的时候 可以使用new 或者overridenew 或者override 区别new创建一个新的方法父类和子类都保留了一份他们是相互独立调用new重写方法时候就看变量的类型变量类型如果是父类调用父类的方法如果是子类调用子类自己的方法。override相当于把父类的方法覆盖掉了不管变量类型是子类类型还是父类类型调用的是子类的方法1. new隐藏 / 独立父类、子类各存一份方法互不干扰调用谁看变量类型变量是父类 → 调父类 变量是子类 → 调子类2. override重写 / 覆盖父类方法被覆盖调用谁看真实对象是谁不管变量怎么写永远调用最终子类的方法internal class Program { static void Main(string[] args) { // 创建的对象如果使用自己类型的变量去接收, 调用的时候调用自己类的方法 //People p new People(); // p.Test1(); // p.Test2(); // p.Test3(); People p1 new Student(); // 找父类类型的变量 接收子类的对象 Student s1 new Student(); //找自己类的类型变量接收对象 p1.Test1();//People的Test1 s1.Test1();//Student的Test1 p1.Test2();//People的Test2 s1.Test2();//Student的Test2 p1.Test3();//Student的Test3 s1.Test3();//Student的Test3 } }public class People { public virtual void Test1() { Console.WriteLine(People的Test1); } public virtual void Test2() { Console.WriteLine(People的Test2); } public virtual void Test3() { Console.WriteLine(People的Test3); } } public class Student : People { public new void Test1() { Console.WriteLine(Student的Test1); } public new void Test2() { Console.WriteLine(Student的Test2); } public override void Test3() { Console.WriteLine(Student的Test3); } }六、密封类和静态类internal class Program { static void Main(string[] args) { //Test t1 new Test(); // Test1 t2 new Test1(); //Test2 t3 new Test2(); } } //抽象类型 不能实例化,用来被继承的 abstract class Test { } //密封类 不能被继承 能够被实例化 sealed class Test1 { } //静态类要求成员都是静态的, 不能被实例化 static class Test2 { public static int Age { get; set; } } //部分类:把一个分成多个类文件进行定义,编译器在编译时候会把多个文件自动合并一个类. // C# 部分类partial class 是 C# 中的部分类核心作用是将一个类的代码拆分到多个文件中编译时编译器会自动把这些文件合并成一个完整的类。 //它的关键字是 partial必须和 class 配合使用。 // 每个文件中的类都必须用 partial 修饰 // 类名、命名空间必须完全一致 // 最终编译后所有部分会合并为一个类 partial class Test3 { } }七、重载和重写区别特点重载 (Overload)重写 (Override)位置同一个类父子类继承方法名相同相同参数必须不同必须相同返回值无关必须相同关键字无virtualoverride