2、大变活人!
(小色喝了口豆浆继续说道,被崇拜的感觉真好啊……)
小色:其实我们所说的描述一个对象,也就是类,就好像是我们所看到的剧本一样,小时候学过老舍的《茶馆》吧?在开篇就介绍一些人物的特征,到后面就是具体去做什么了。可是看书总会觉得不真实,于是人们用话剧的形式将其演义出来,这其实就是一个“大变活人”的过程。若结合到面向对象语言中,这就是一个实例化对象的过程。简单的说,就是描述一个对象后并不能直接使用,需要经过实例化后,让其在内存中真正存在这个对象后才可以使用,这个过程也可以被称作创建一个对象的实例。
小力:说这么多概念,那怎么就能让她做个小菜吃吃呢?
小色:别着急,这就来了。我们要想创建一个对象实例,就需要使用new这个关键字了。还记得在程序中我们怎么声明一个变量吗?
小力:数据类型 变量名称 = 初始值 ;
小色:对,那如果要做的如果是一个Girl类型的实例就该写成 Girl lili=new Girl( );
小力:不对不对,我们声明一个变量比如int a=32;就可以了,没有像你这个这么复杂啊,还要new的。
小色:你看的真仔细啊,其实这个就涉及到值类型和引用类型的内容了,我们常用的int是属于值类型,继承自结构类型,而我们现在所用的是引用类型,具体的内容我们下回见面时再告诉你(窃喜!)。而上面那段代码的意思实际是“在内存中开辟一块可以放置Girl内容的空间叫做lili (Girl lili),然后在堆中创建一个Girl的实例(new Girl( ) ),将实例引用指向至lili(等于号)”,这样我们操纵lili这个对象,就好像大变活人一样让她做事情了。由于她已经是一个实例,所以我们还可以给她的实例变量赋值,调用她的实例方法了。
小力:恩,你讲的我好像已经明白点了,那就操作一次试试啊!
小色:想要调用实例变量和实例方法,其实很简单,只需要通过“.”点运算符就可以了,可以把它理解成我们汉语里“的”和“在”这两个字,以前我们都是自己调用自身的数据所以没有用过这个符号,现在我们有对象了,就可以说成“lili的年龄”、“lili的名字”、“lili在做饭”、“lili在做自我介绍”,写成代码就是这样:
using System; using System.Collections.Generic; using System.Text; namespace Lesson1 { /// <summary> /// 这个是测试类 /// </summary> class Program { static void {//程序主入口 Girl lili = new Girl();//实例化一个对象 lili.name = "莉莉";//给实例变量姓名赋值,注意string类型使用双引号 lili.sex='女';//给实例变量性别赋值,注意char类型使用单引号 lili.age = 23;//给实例变量年龄赋值 lili.high = 168;//给实例变量身高赋值 lili.weight = 105;//给实例变量体重赋值 lili.Introduce();//调用自我介绍方法 lili.Cook();//调用做饭方法 Console.ReadLine();//等待用户输入内容再结束程序,否则窗口将瞬间消失 } } } |
运行结果如下:
我叫:莉莉,性别女,今年23岁,身高 红豆,大红豆,芋头~~~锉锉锉锉…… |
小力:哇,真的在做菜东西了。咳,这不是“锉冰进行曲”吗?
小色:是啊,我们上面写了那么多的实例变量赋值,如果把那些内容删掉,不给她赋值,又会成什么样子呢?
运行结果如下:
我叫: ,性别 ,今年0岁,身高 红豆,大红豆,芋头~~~锉锉锉锉…… |
小力:好奇怪啊,怎么姓名和性别都是空的了,而数字类型的都变成了0了呢?
小色:呵呵,这是因为实例变量自己是有初始值的。所以即使我们不给它赋值,也能显示出来,而写在方法里的变量,也就是局部变量,若不赋值就被使用,是会报错的。下面简单列举一下数据的默认初始值:
类型 |
初始值 |
Int |
0 |
Double |
0.0 |
String |
null |
Bool |
False |
Char |
\0 |
对象 |
null |
小力:哈哈,这样的话,你的对象就算是正式出现了,我们的程序也写完了吧?!
小色:想的美啊你,要是程序能这么简单的话,那我们的世界岂不是太精彩了。后面的问题就接踵而来了。
小力:还有问题?这不程序写的挺好吗!
小色:你仔细想想啊,如果我们给她赋值了一个年龄为-50岁,怎么办?还有女孩子的体重是不是只能自己知道,别人要是猜的少的话还行,乱猜的话是不是肯定得挨骂啊?小力我看你怎么也得有一百二十多斤吧!?
小力:滚!
小色:……骂人也得把米饭咽下去了再骂啊,服务员,来点餐巾纸!
小力:照你这么说还确实是这样,那要是想改的话,怎么改呢?
小色:这就是我马上要给你讲的属性(Property)和访问修饰符,你等等,我先去洗把脸……
小色:先说一下访问修饰符,其实就是为了限制给定类或结构的一个成员(方法、字段、构造函数等)的可见性级别,在C#中的可访问性关键字有如下几种:
C#访问修饰符 |
作用 |
public |
被任意存取,既可从一个对象变量访问,又可以从任何子类访问 |
private |
仅可以被定义这个成员的方法或类访问 |
protected |
可以在被定义这个成员的方法或类中访问,也可从子类访问 |
internal |
仅能在程序集内被访问 |
protected internal |
仅在当前程序集,或当前程序集的子类 |
被声明为公共成员能被对象引用直接访问。私有成员不能被对象引用所访问,但它能被对象内部调用,以帮助实例完成它的工作,受保护的成员仅仅在创建类层次的时候才有实际的用处。若成员变量或方法不屑访问修饰符,则默认为Private,若类不写访问修饰符,则默认为internal
小力:还真麻烦啊,能举个例子吗?
小色:没问题啊,写个简单的吧。
class VisibilityClass { //可以从任意地方访问 public void PublicMethod() { } //仅可以从本类中访问 private void PrivateMethod() { } //可以从本类和本类的派生类中访问 protected void ProtectedMethod() { } //可以在同一个程序集中访问 internal void InternalMethod() { } //在程序集中受保护的访问 protected internal void ProtectedInternalMethod() { } //没有标记访问类型,C#默认为private私有的 void SomeMethod(){ } } |
小力:恩,我明白了,也就是说public的权限最大就好像公共厕所谁都能用;internal比public的权限小一点,就好像是咱们家里的厕所,只有属于我们家里的人才能用;protected比internal权限小一点,就好像我爸爸买给我家领养的弟弟的小汽车,只有能继承我们家的行业才能给他用;而protected internal又比protected权限小一点,就想我妈妈传给我的传家宝,只有拥有我们家血统的我才能使用。private权限最小就好像是内衣裤只有自己才能穿。你说我这么理解对吗?
小色:啊对,虽然乱了点,但是这么理解也没什么错误。算是开窍了吧!
小力:那这个又跟你之前说的赋值啊属性什么的有什么关系啊?
小色:重点就在这里了,我们要是想赋值,但是万一赋值错了,那就一点补救办法都没有了,所以我们要以防万一,就需要在赋值的时候顺便要做验证,如果是用JAVA来赋值和取值,就必须做两个方法,在赋值的方法里需要写上if判断语句,来检查是否符合规范,如果不符合则如何如何。取值的时候写上返回类型,return要取到的内容。现在你试试用java写一个简单的取到对象的年龄并且给她增加一岁再赋值回去的代码吧。
小力:我写完了,你看看!
//java版本的关键代码 Girl Lili = new Girl();//实例化对象 Lili.SetAge(Lili.GetAge() + 1);//通过GetAge()方法取值并加1,再通过SetAge()方法赋值 |
小色:这样会不会觉得很麻烦啊?!现在我来写一段,你再感觉一下。
//C#版本的关键代码 Girl Lili = new Girl();//实例化对象 Lili.Age++;//通过属性取值并且在其自身上自增1 |
小力:哇,这样用就太简单了。少了两个方法,就一个步骤就行了。快点教我快点教我嘛……
(小色虽然满身鸡皮疙瘩,但是依然暗爽……)
小色:好啊,其实属性并不难,先考你三个英语单词:get、set、value
小力:分别是获得、放置和值,我可是念过大学的哦~~别小瞧我。
小色:不错,大学生果然不是盖的。不错,我们就要使用这三个关键字来定义属性,基本结构是这样的:
private 数据类型 成员变量; public 数据类型 属性名称 { get { return成员变量; } set { 成员变量 = value; } } |
在这里我们就以年龄为例,来修改咱们刚才的代码吧:
//…省略部分代码…// private int age;//由public改为private public int Age { get { //访问器用来取值 return age; //将变量age的值返回 } set {//修改器用来赋值 if (value > 0 && value < 100) {//如果值在0到100之间则允许赋值 age = value;//把接收到的value赋值给age变量 } else {//否则自动默认赋值18 age = 18; } } } //…省略部分代码…// |
由于字段被设置为私有的,所以属性的使用必须先实例化相应对象,get访问器用来返回字段的值,set访问器用来设置字段的值,value是一个在属性集作用域内属性赋值期间用来标示隐含参数的上下文关键字,其实也就是要设置的值。(在VS中可通过打入prop+两次TAB键生成属性块)
若代码为:
lili.Age = 23;//给实例变量年龄赋值 Console.WriteLine("{0}的今年{1}岁,明年{2}岁",lili.name,lili.Age,lili.Age+1); |
则结果为:
莉莉今年23岁,明年24岁 |
小色:这其实就是面向对象三大特性:封装、继承、多态中的封装。封装的作用就是用于隐藏内部的实现,对外暴露对类的基本操作,而不会让其他对象影响类的内部实现。封装的好处,就是可以避免使用非法数据赋值、保证数据完整性、还可以避免类内部发生修改时从而导致整个程序的修改。
小色:set修改器和get访问器这两种其实分别对应着写和读的操作,他们可以缺省任意一个,比如只想赋值但是不允许取值的话,就将get块删掉,如果只想取值却不想被赋值的话,可以将set去掉。这样程序就变成了只读或只写了。
小力:对对对,这样的话,我们赋值就不会赋错了,如果修改的话,我就把全部内容都改成属性的方式,省的让别人随意去赋值,如果我不想让别人乱猜我的体重的话,我就去掉set设置器,这样别人就只能知道我的体重,而不能给我设置体重了。
小色:恩,属性与传统的访问方法/修改方法对的使用目的是相同的。属性的优点是对象的用户只使用一个命名项就能操作内部数据。其实属性的内容还有静态属性、分别对get/set设置访问级别这些内容,如果你想继续深入的话,就去伟大的“Baidu都知道”去查查吧!
小力:辛苦了辛苦了,快点喝口豆浆吧,再不喝都凉了!
小色:…我要的是冰豆浆…
小力:要不咱们换个地方继续说,这个地方太吵了。服务员,买单!
小色:别别,让美女请客多不好意思啊。买单,还是我来吧!等等,我又想起来一点东西,讲完咱们再走啊。好不容易能跟美女一起聊天,我想多说会,太吵的话,你离我近点就行了。
小力:好吧!
(美女往身边移了移,小色幸福的感觉如同摸到了高压电,爽遍全身,症状也如触电般口水直流目光呆滞……)
完稿日期: