C#面向对象(四)
C#面向对象
面向对象思想:面向过程的编程常常会导致所谓的单一应用程序,即所有的功能包含在几个模块(常常是包含在一个代码模块中)。而使用oop(面向对象)技术,常常要使用许多代码模块,每个模块都提供特定的功能,每个模块都是孤立的,甚至与其他模块完全独立。这种模块化编程方法提供了非常大的多样性,大大增加了代码的重用机会。
在传统的应用程序中,执行流常常是简单的、线性的。把应用程序加载到内存中,从A点开始执行到B点结束,然后从内存中卸载,这个过程可能用到其他各种实体,例如存储介质的文件或视频卡的功能,但处理的主体总是位于一个地方。而使用oop,事情就不是这么直接了,尽管可以获得相同的效果,但其实现方式是完全不同的。Oop技术以结构、数据的含义以及数据和数据之间的交互操作为基础,这通常意味着要把更多的精力放在项目的设计阶段,但项目的可扩展性比较高。一旦对某种类型的数据的表达方式达成一致,这种表达方式就会应用到应用程序以后的版本中,甚至是全新的应用程序中。这样将大大减少开发时间。
(一)类:
类描述一组相似对象的共同特性。类和结构实际上都是创建对象的模板,并提供了处理和访问数据的对象。
类定义以下要素:
1. 定义该类的对象所操作的数据结构(attribute的名称和类型);
2. 这些对象要执行的操作,尤其是哪些操作,类的对象如何被调用执行这些操作,以及对象执行这些操作是的“后台运作”。
注:在类里面声明变量必须说明变量是共有的还是私有的或着是保护的。
public int i;//声明了一个int变量,而这个变量是共有的,就是说,在客户端只要实力化对象后就能在客户端使用他了。如果定义成私有的provate,那么在客户端就算实力化对象后也不能使用他。
类的定义:
(二)什么是对象
对象就是oop应用程序的一个组成部件。这个组成部件封装了部分应用程序,这部分程序可以是一个过程、一些数据或一些更抽象的实体。
C#中的对象是从类型中创建的,就像前面的变量一样,对象的类型在oop中有一个特殊的名称:类。可以使用类的定义实例化对象,这表示创建该类的一个实例。
“类的实例”和对象表示相同得的含义,注意“类”和“对象”是完全不同的概念。
注:类是一系列实物的共体的抽象,而对象才是真正的实体。类是将相同实体抽象的描述为某一类别的事物。
如:将所有学生抽象为一个类,所有老师为一个类,而每个学生是学生类的一个类的实例(类是抽象的,具体的学生才是真实存在的),或说学生类的一个对象,所以说对象是类的一个实例。(实例:一个真实存在的例子)。
所有学生都有课程,假期,书本,上课,下课,选课等相同特性,先将他们抽象为一个Student类在Student类里面有上面这些属性。但是类本身不能实现自己啊,他是不真实存在的,所以要实例化一个对象创建了一个对象后就好像创建了某一个学生,假如我是这个学生,那么我就可以真正的去上课,去选课,但是Student类本身不能去上课,去选课。只有实例化化后(如同我实例化后,就相当于该类的一个实例)我是该类的实例(真实存在的一个例子)那么我就可以去上课,选课了。
对象是真实存在的,而类是对某一类具有相同特性的对象的一个抽象模型。
注:对象只能做所属类已定义的方法所规定的操作。
如:现在本田公司定义一个汽车类,这个汽车类里面定义了轮子、螺丝、底板、方向盘、玻璃、座椅、刹车、油箱等。而你是设计师,在公司汽车这个类里面你要创造出一辆汽车来。轮子、螺丝、底板等那些都是公司里面现有的,你现在要创造一辆汽车,你不能用公司里面没有的东西,而你创建出一辆汽车的时候,这辆汽车就是汽车类的一个真实存在的例子,他是真实存在的。
对象和类的关系进一步说明:类就好像是一个byte类型,byte类型规定被定义为byte型的变量只能在0——256之间。超出这个范围就溢出,程序就会出错。
类也是一样,规定一些范围,然后用类名来定义一个变量(准确点说应该是引用变量,因为类定义的变量是引用行的)时,这个变量也只能在类的范围里,而用new关键字来真正的在内存中创建一个全新的对象,这个全新的对象里面放着类里面的字段、属性、方法,每新建一个对象。
就如:我是房地产商,在我卖房这前,我先建立一个房子模型告诉顾客这片地将建成什么样的房子(就像定义一个类,类里面有些什么方法,属性,字段),他听后就赋钱给我了,我就用这些钱在这块地上盖一栋真实的房子(就好像用new在内存中创建了一个对象)。我只有一个模型,所有给我买房的人他们的房子都是照着这个模型来做的,建出来的房子和这个模型一模一样,他不会多什么,也不会少什么,只不过模型不能用,而照着模型新建的房子才能用,所以所有买房的人都会得到一栋相同的相互独立的房子。付了钱的顾客可以根据房子的物理地址来找到他的房子(因为每新建一栋房子,房子物理的地址都会不同),当付了钱的顾客通过物理地址找到房子后,他可以可以随便用房子里面的东西,他也可以可以将房子装修成自己喜欢的样式。
解说:public class Home
{
private string 沙发;
private string 们;
private string 电视;
……
}
Public class 售楼部
{
Public static void main(String arge[])
{
Home 顾客1;//没有房子,因为他们是来看的,还没有买房;
Home 顾客2;
Home 顾客3;
new Home();//在地上照着Home类这个模型建了一栋真实的房子
Home 付了钱的顾客=new Home();//付了钱的顾客我就给他一懂真实的房子。
//当“顾客
顾客1=new Home();//我也给他一栋新房子;
}
}
注:每new Home()一次,都会在没有被占用的内存中新建一个对象(在空余的地皮上新建一栋房子),而不是替换原来的对象,所以每个对象都是互相独立在内存中的,他们互不干涉。
Home 顾客1;//这里并没有创建一个对象,只是定义了一个引用变量。
New Home();//这里在内存中创建了一个对象。(在内存中创建那么说明他是占内存的)。
顾客1=new Home(),将“顾客1”的引用指针指向new Home()创建的对象的内存地址。
对于new Home()没有引用变量指针的指向。那么c#会自动将从内存中销毁,防止占用内存。因为他没有引用变量指针的指向,他虽然占用内存,真实存在,但他没有被用到。那就是无用的对象,所以要将他销毁。
(三)成员定义
在类定义中,也提供了该类中所有成员的定义,包括字段、方法和属性,所有的成员都有自己的访问级别,下面的关键字之一来定义:
最后两个关键字可以合并使用,所以也有protected internal成员。它们只能由项目中派生的代码来访问。
字段、方法和属性都可以使用关键字static来声明,这表示它们是类的静态成员,而不是对象实例的成员。
(三)属性和字段
属性字段可以访问对象中包含的数据,这个对象数据可以用于区分不同的对象,因为同一个类的不同对象在属性和字段中存储了不同的值。
1.字段
在asp.net中,公共字段以PascalCasing形式来命名,而不是camelCasing。这里使用这种命名方法,。
字段用来存储与类有关的数据,字段是与类相关的变量。
字段和变量的区别:直接在类中的数据成员为字段,他用访问修饰符和数据类型类定义
(public string name;),字段就像类的一个小数据库,用来存放与类相关的数据;而单纯的变量是没有修饰符的(int age;),他不能直接在类里面定义,只能在函数里面定义,他用来作为方法的一个临时变量。
字段也可以使用关键字readonly,表示这个只读字段只能在执行构造函数的过程中赋值,或由初始化赋值语句赋值。
Class MyClass
{
Public readonly int MyInt=12;
}
2.属性的定义
属性用来隐藏字段,提高字段的安全性。
属性拥有两个类似于函数的块,一个块用于获取属性的值,另一个块由于设置属性的值,
这两个块也称为访问器,分别用get和set关键字来定义,可以用于控制对属性的访问级别。可以忽略其中的一个块来创建只读或只写属性(忽略get块创建只写属性,忽略set块创建只读属性)。还可以将set设置为受保护的,get设置成公共的。属性至少要包含一个块,才是有效的。
Get块必须有一个属性类型的返回值。简单的属性一般与一个私有字段相关联,以控制对这个字段的访问,此时get块可以直接返回该字段的值。
Private int myInt;//一个字段
Public int MyIntProp//一个属性
{
get { return myInt;}
set {myint=value;}
//value等于类型于属性相同的一个值,所以如果属性和字段使用相同的类型,就不必担心数据类型转换了。
}
注:属性可以使用virtual,override和abstract关键字,就像方法一样,但这几个关键字不能用于字段。
public class person //定义基类
{
private string name;
private string adress;
public string Name
{
get { return name; }
set {name = value; }
}
public string Adress
{
get{return adress; }
set{adress = value; }
}
public void print()
{
Console.WriteLine("姓名: " + Name + "\n" + "地址" + Adress);
}
}
public class Student:person //继承person类
{
private string studentId; //定义一个字段
public string StudenId//定义一个属性
{
get{return studentId; }
set{ studentId = value; }
}
public void print1()//定义一个方法,但是不能和基类的方法重名哦!如果和基类的重名了,那么会把基类的方法给隐藏掉。如果真的想隐藏掉基类的方法,那么可以在前面加一个new关键字。
{
Console.WriteLine("学号" + StudenId+"\n");
}
}
public class Professor:person
{
private string title;
public string Title
{
get{ return title; }
set{title = value; }
}
public void print1()
{
Console.WriteLine("官位 " + Title);
}
}
class Program
{
static void
{
Student s = new Student();
s.Name = "报文";
s.Adress = "云南农业大学";
s.print();//调用基类中的方法;
s.print1();//调用自己类中的方法;
Professor p = new Professor();
p.Title = "教授";
p.Adress = "云南农业大学";
p.print();
p.print1();
Console.Read();
}
}
(四)方法的定义
(五)对象的生命周期
每个对象都有一个明确定义的生命周期,除了“正在使用”的正常状态之外,还有两个重要的阶段:
构造阶段:对象最初进行实例化的时期,这个初始化过程称为构造阶段,由构造函数完成。
析构阶段:在删除一个对象时,常常需要执行一些清理工作,例如释放内存,这由析构函数完成。
(六)访问器
1.访问器
访问器包括:get取值器(外面从get取值)、set赋值器(外面赋的值先赋给set,然后通过set赋值给私有变量)
class Program
{
static void
{
Person1 p1 = new Person1();
19行: p1.Age = 39;//赋值
20行: p1.Age = p1.Age + 1;// 右边的p1.Age要从Person1中取值,但是person1只返回了一个3给我,那么我就用3加1(39怎么也用不到了),然后再对左边的p1.Age赋值,因为给person1的Age赋值要通过Age的set赋值器来赋值,但set不接受外界传过来的值,无论外界怎么赋值都相当于没有赋值;
Console.WriteLine(p1.Age);// 这里输出的结果是3;p1.Age现在要输出了,那么他要先将值取出来才能打印啊。所以又要到person1中去拿,结果person1中的取值器get返回给我个3;所以打印出一个3来;
Console.ReadKey();
}
}
class Person1
{
31行: private int age;//set接受进来的值存在age里面;而这里不用age来存值,因为set什么也不接收所以也用不着存值。
33行: public int Age
{
get //set大哥给我个值我想用就用,不想用我就自己给自己个值;当用户来想我拿值的时候我可以从set大哥那里拿来的,也可以用我自己的;
{//不管谁向我要值(20行,21行向我要值)我都给他个3,这里我不返回age中存储的值;
return 3;
//return age;返回存储里面的,也可以返回其他值;
}
37行: set//用户给的东西要不要由我说了算;如果我要的话,get就可以从我这里拿,get也可以不拿自己给自己一个值;
{//对用户端的值(第19行,第20行对我赋值)我不踩取,我什么都不要;
// age = 10;age可以等于自己定义的一个数值,也可以等于用户传过来的值
//age=value,value是用户赋过来的值(age=Age=value=P1.Age=10)。
注:P1.Age=10用户对Age赋值;Age没有内存空间,他只是一个访问器,所以将传进来的值放到了age(31行定义了age,就为他分配了内存空间);
}
}
}
步骤分析:person1的执行过程为:19(我给37赋值),37(19给我个值但是我不要),20(去33行取个值),33(返回个3给他),20(3+1等于4赋值给37行),37(我还是不要),21(我想输出p1.Age请给我个值,去33行拿),33(我这里只有3,给你)
总结:1.从上面这个例子说明;set是用户给他赋值,在外面拿来的值,最终要交给set。
2.而get是用户来向他取值的。当外面要用值的时候,就来get这里拿。而get所给用户的值可以是自己给的也可以是从set大哥那里拿来的;
3. 当定义为私有的时候通过公有方法可以将值从外面传进来,而这个公有方法可以对外面的值做一个过滤,可以接受、不接受、还可以接受后随意改变。
4.私有变量外面需要通过公有方法才能传进来。像上面的set是不接受外面的值的,但是里面他自己可以更改;里面更改后可以随意的用而不用问get去拿。外面要用类里面的私有值的时候必须经过get来拿,get给什么他就拿什么,但是在类本身里面他可以不用从get哪里拿,他可以自己随意改变,而不用管set是否接收;
对第4点总结一句话:get;set是外面对私有变量赋值与取值的转换器,而对类自己里面不用通过他们,直接赋值,直接取值;外界赋通过set赋进来的值被放在了私有变量中了。当类自己为变量赋值或取值时,类里面说了算,外面赋值相当于给了私有变量一个初始值,如果类里面自己对他从新赋值了,然后再取值,当然取到的是新值了。
注意:c#里面如果类中的变量如果没有赋初值也可以用,他的默认值为0;
2.访问器的过滤作用
在set、get里面这个一些条件来达到过滤的作用,如果变量定义为公有的,那么这个变量的get、set就不用写了,用户可以直接给公有变量赋值,而不用通过访问器来赋值,所以get、set也就起不了作用了。
class Program
{
static void
{
Person p = new Person();
p.Age = 10;
p.Sex = "女";
p.Name = "小王";
p.Point();
}
}
class Person
{
private int age;
private string name;
private string sex;
public int Age //Age不保存值,通过set将值保存在age里面;
{
get //取值
{
return age;
}
set //赋值
{
if (value < 0)// 如果是公有的,那么在这里设置就不起作用了。因为公有的可以直接在用户端赋值,而私有的必须通过公有方法才能将值传递进来;
{//这里相当与设置了一个私人的过滤器,自己可以选择自己想要的,如果是公有的就不可以了。
Console.WriteLine("你输入的值不符合要求");
return;
}
age = value;// value是用户赋值过来的值;
}
}
public string Name
{
get//因为是name是私有的,在这里可以将拿来的值进行添、删、改或直接不要,一切有我get说了算。
{
if (name == "大王")//假如我拿到的name==大王,当用户向我要值的时候我就在name上添加一个总经理;
{ return name + "总经理"; }
else
{ return name; }
}
set
{ name = value; }
}
public string Sex
{
get
{ return sex; }
set
{
if (value == "女")
{
Console.WriteLine("对不起,小姐,这里是男人俱乐部");
return行: return;// 这里的返回只是返回Sex外面去,不执行Sex里面在return之下的语句;
}
sex = value;// 如果是女的就不可能将值赋给sex,前一步就直接被返回去了。
}
}
public void Point()
{
Console.WriteLine("你好{0},欢迎来到儿童俱乐部,你有{1}岁,你{2}",Name,Age,Sex);
}
}
注:如果私有变量时字符串型,我们没有给他赋值,则默认为null;而整型的话,没有赋值,则默认为0。这个的sex=女,当执行到“return行”是就被返回去了,那么就没有对set赋值了。所以set为空。
3.引用型与值传递
值类型的值是放在线程堆栈上的,在堆栈上没有“同步快”和“方法表”只有值;
引用类型的值是放在托管推上的,在托管堆上有“同步快”和“方法表”加上引用类型的值。
值传递是将一个值赋值过去。
//------------------------------值传递
int i = 10;
int j = i;// j复制了i的值;当i在j=i值后改变时j并不会改变;
i++;
Console.WriteLine(j);//j当然是10啦。
//-------------------------------引用型
class Program
{
static void
{
Person p1 = new Person(5);
Person p;
p = p1;// p,p1指向了同一个对象;这就是引用;
p1.Age++;//p1.Age++他令p,p1他们同指的对象改变了; 所以p.Age也跟着改变(因为他们指向同一个对象);
Console.WriteLine(p.Age);
Person p2 = new Person(10);
p = p1;
p1 = p2; //p1指向了p2所值的对象,他并没有改变原先的对象;而p现在还是指着原来的对象;所以p.Age并没有改变;
Console.WriteLine(p.Age);
Console.ReadKey();
}
}
class Person
{
//public int age1;//一个变量
public int Age { get; set; }//Age是person的一个属性;而不是一个变量;
public Person(int age)
{
this.Age = age;
}
}