C#面向对象
C#面向对象(一)
一:面向对象的基本知识
C#程序分为面向过程和面向对象
什么是对象:一切皆为对象:Object,生活中常说的“东西”就是程序里面所指的对象;生活中遇到的东西我们都在下意识的归类;归类意味着抽象模型;
类:class,对某类众多对象的共同特点抽象出来的模型。
他们的关系:类是好多对象的抽象,对象是类的实例化。
创建一个名为Dog的类:
class Dog //一般首字母大写 { int age; public void setage(int a) //用方法赋值 { age = a; } public void buck() { Console.WriteLine("这是一个方法"); } }
类中一般包括两类东西,变量(名词,也交成员变量这里age是成员变量)和函数(动词,也叫成员函数或成员方法 buck()就是方法)。
对象实例化:
Dog d1 = new Dog();
这样就实例化出了一个Dog的对象a。
例子:两个套在一起的圆,求内圆的周长和内圆与外圆之间的面积,用面向对象的思想做
class circle { float r; public circle(float a) { r = a; } public double zhouchang() { return 2 * 3.14 * r; } public double mianji() { return 3.14 * r * r; } } class Program { static void Main(string[] args) { circle m = new circle(10); circle n = new circle(20); double bc = m.zhouchang(); double mj = (n.mianji()-m.mianji()); Console.WriteLine("内圆的周长为:"+bc); Console.WriteLine("花砖的面积为:"+mj); } }
首先做了一个名为circle的类,它可以生产任何一个半径不同的圆,在构造函数中,为半径赋值。
二:面向对象的三大特性
三大特性是指:封装,继承,多态。
类中的方法一般分为:构造方法(函数);属性方法(函数):成员变量赋值取值;行为方法(函数):变量运算。
(一): 封装
1.封装含义:
(1)不同类的变量只属于各自的类。
(2)不同对象的成员变量只属于各自的对象,彼此不受影响。
(3)对象中的变量需要通过方法(函数)实现操作,比较安全。
封装为了安全,尽量不用public来声明变量,避免在main函数中可以直接访问赋值而降低了安全性,在类中建立public的方法来赋值,main中调用此方法传值。
2. 成员变量及访问修饰
private 私有成员 ,protected 受保护的成员,public 公有成员
3. 构造函数
它是一个特殊的成员函数,一般在构造函数里进行初始化。如果不写构造函数在new的时候会自动生成一个默认空的构造函数。
写法特殊:没有返回值,函数名只能与类名一样;public 类名(){};
执行特殊:类在实例化(new出来的时候)自动执行,构造函数是最早执行的成员函数,构造函数是用来生成对象的函数。
它的主要作用:对象实例化生成的时候,做一些初始化的工作。
下面例子就是做了一个Ren()的构造函数,为其变量赋上初始化的值:
class Ren { string _Name; int _Age; public Ren() { _Name = "龙神"; _Age = 25; } }
4. 重载(函数或方法)
函数名相同,参数不同(参数个数或类型不同)的多个函数就形成了重载。
重载只与函数名和形参有关,与返回类型无关。
下面举个例子,构造函数的重载:
class Ren() { string _Name; int _Age; public Ren() { _Name = "ZSMJ"; _Age = 25; } public Ren(string name) { _Name = name; } public Ren(string name,int age) { _Name = name; _Age = age; } }
这样Ren类中就做了三个构造函数,第一个是无参的,第二个是一个string类型的参数,第三个是两个参数,类型分别为string和int。他们符合重载的条件,所以会形成重载在Main函数中new的时候根据参数的不同会自动选择执行其中一个构造函数。
如果在Main函数中:
Ren a = new Ren("王大锤");
则执行的构造函数是第二个。
5. 属性
它的声明:public 类型 属性名;也可以选择成员变量右键重构,封装字段来生成属性方法,例如:
string _Name; public string Name { get { return _Name; } set { _Name = value; } }
这样Name就是该类的一个属性,在Main函数中可以用此属性为其成员变量赋值取值:
Ren a = new Ren("王大锤"); a.Name = "王尼玛";
注意:(1)属性是用来为成员变量赋值和取值的,它有代替属性方法的作用,一般用属性。
(2)属性定义的时候,属性名后面没有小括号。
(3)属性都是public。
(4)属性中只能包含两个部分:get和set。代码也只能写在get和set的花括号里面。
(5)属性分为只读属性,只写属性和可读写属性,与get和set的有无有关系。
6. this关键字
概念:this引用,this在哪个对象里面执行代表该对象本身。
用法:this.成员变量(成员方法),this._Name; this.Eat();
例子:this 调用当前对象的其它构造函数。
public class Mydate { int _Year; int _Month; int _Day; int _Hours; public Mydate(int year,int month) { _Year = year; _Month = month; } public Mydate(int year,int month,int day,int hours): this(year,month) { _Day = day; _Hours = hours; } }
这里第一个构造函数中有两个参数year和month,在做第二个构造函数的时候为了方便用“:this”来调用了本类中第一个构造函数,这样第二个构造函数只需写后day和hours的执行语句就可以了。
在new的时候传入不同的参数,会实例出不同的对象,这时候的this就代表不同的对象。
7. is关键字(运算符)
用法:对象 is 类名;is左边是对象,右边是类型;
Console.WriteLine(a is Ren);
如果a是一个Ren类的对象,则返回值为true,否则返回值为false。
8. partial关键字
如果一个类特别大,不宜挡在一个文件中实现或者一个类中有一部分代码不宜与其他混淆或者需要多人合作编写一个类,这就需要将一个类拆开来写。
非静态变量称为实例变量,非静态方法称为实例方法,实例成员的数据存在每个对象中,用对象名来调用。
静态成员包括:静态变量,静态属性,静态方法。
定义一个成员为静态的:在变量或方法前加static,如:static int a;
静态变量是属于类的,每个对象都有的并且相同的东西只保存一份,不和实例变量那样在每个对象里都面保存一份。
可以说它不属于任何对象,也可以说它又属于任何一个对象,给每个对象用,节省空间。
例如:每包粉笔的颜色是静态成员,每支粉笔的剩余长度是实例成员。
静态变量或方法不需要new出来。
在C#中,定义了一个粉笔的类:
class Fenbi { static string _Color; public static string Color { get { return Fenbi._Color; } set { Fenbi._Color = value; } } int _Lenght; public int Lenght { get { return _Lenght; } set { _Lenght = value; } } }
(1)在当前类的花括号外,静态成员只能用类名来调用,不能用对象名来调用,而实例成员只能用对象名来调用,不能用类名来调用。
Fenbi.Color ="Yellow"; Fenbi b = new Fenbi(); b.Lenght = 10;
(2)在当前类的花括号内,静态方法只能直接调用静态成员,不能调用非静态成员,实例方法可以调用非静态和静态的成员。
public static void xiezi() { Console.WriteLine("写出"+_Color+"的文字了"); }
public void change() { Console.WriteLine(_Color+"的粉笔长度变为:"+_Lenght); }
10. 拷贝
浅拷贝:传递引用,不赋值对象。
深拷贝:创建一个新的对象。
(二): 继承
1. 语法:public 子类名:父类名;如:class Dog:Pet。
2. 特点:单继承,一个父类可以派生多个子类,但每个子类只能有一个父类
如果一个类,没有明确指定父类是谁,默认是object。除了object类之外,所有类都有一个父类。
子类可以从父类继承下父类的成员变量和成员方法。
3. 访问修饰符合访问权限:
private 的成员不被继承,只能在该类中访问。
protected成员可以被继承,能在该类和派生类中访问到,在外界访问不到。父类中的变量一般用protected。
public成员可以被继承,能在所有地方访问到。
4. base 关键字:子类中可以用(base.父类中的成员 )来调用父类中的成员,base()调用父类构造,base.xxx()调用父类成员方法。
调用的参数值会被覆盖,方法也会被覆盖。
5. 继承关系中实例化子类的流程:
先执行父类的构造函数,再执行子类的构造函数。
6. 继承关系的实例化:
如果父类的构造函数没有空参构造函数,全是带参数的,则子类必须要写构造函数,构造函数的形参中必须包含父类构造函数所需要的参数,还要用base()把父类构造函数所需要的参数传递父类。
例如:父类中只有这个构造函数
public Ren(string name, int age) { _Name = name; _Age = age; }
那么子类中的构造函数应该这样写:
public Chinese(string name, int age, string yuyan):base(name,age) { _Yuyan = yuyan; }
7. sealed 关键字:
如果用来修饰class,称为密封类,此类无法被继承;如果用来修饰方法,该方法无法被重写。
如: sealed class Ren{};
(三): 多态
1. 概念:父类引用指向不同子类实例的时候,父类引用所调用的函数都是子类的函数,由于子类对象不同,父类引用调用的成员表现出来的不同状态就是一种多态。
2. 实现的方式:多态需要通过继承来实现
3. 分类:分为编译多态(重载overload)和运行多态(重写override)。父类方法被重写了之后也可以在子类中用base.方法 调用。
4. virtual关键字:虚方法,允不允许重写,要重写父类方法必须是虚方法:public virtual void Eat()。
5. 运行多态实现的条件:
(1)子类对父类方法的重写(override),父类和子类中都有相同的方法。
(2)父类引用指向子类实例。
例如,有一个Ren类是父类,一个Chinese类和American是子类,Ren r = new Chinese();父类的引用 r 指向子类实例。
class Ren { protected string _Name; protected string _Country; public virtual void Eat() { Console.WriteLine("正在吃饭..."); } } class American : Ren { public override void Eat() { Console.WriteLine("正在用叉子和刀子吃饭...."); } } class Chinese : Ren { public override void Eat() { Console.WriteLine("正在用筷子吃饭..."); } }
父类Ren中有个Eat方法为虚方法,在子类Chinese和American中进行了重写,在Main函数中:
Random rand = new Random(); int n = rand.Next(100); Ren a; if (n % 2 == 0) { a = new American(); } else { a = new Chinese(); } a.Eat();
随机让父类引用 a 指向 不同的子类实例,这样用父类引用 a 调用方法Eat()的时候表现出不同对象的操作。
6. 里氏代换原则和抽象依赖原则
里氏代换原则,如果某个方法接收的是父类引用,可以向里面传父类或其子类的元素,子类对象替代父类对象。
抽象依赖原则,用父类的引用来指向子类的实例。
例子:怪兽吃人,传入Ren的引用 r ,则r.Cry()表现出来不同的结果。
class Monster { public void EatFood(Ren r) //r = a; { r.Cry(); Console.WriteLine("人类真好吃,吃饱了!"); } } class Ren { public virtual void Cry() { Console.WriteLine("......."); } } class American:Ren { public override void Cry() { Console.WriteLine("MyGod,God bless me!"); } } class Chinese:Ren { public override void Cry() { Console.WriteLine("天哪,老天爷保佑我!"); } }
在Main函数中 ,先实例出一个怪兽,随机生成一个Ren的对象,将此对象的引用传入怪兽类里,通过这个引用来表现出不同的状态。
Monster m = new Monster();
Random rand = new Random(); int n = rand.Next(100); if (n % 2 == 0) { American a = new American(); //或者是这样写 Ren a = new American(); m.EatFood(a); } else { Chinese c = new Chinese(); //或者是这样写 Ren c = new Chinese(); m.EatFood(c); }