《叩开C#之门》系列之四Word格式.docx
《《叩开C#之门》系列之四Word格式.docx》由会员分享,可在线阅读,更多相关《《叩开C#之门》系列之四Word格式.docx(12页珍藏版)》请在冰点文库上搜索。
而下面的两行代码则是正确的:
radius=2.5;
类的字段其实也是变量。
如系列三中的类User,就包含有字段m_name,m_password,m_tryCounter。
它们
的类型分别为string,string,int。
字段仍然可以利用public,internal,protected,private来修饰
它。
不过,我建议如非特殊情况,不要将字段修饰为public。
因为,根据”对象封装”的原则,应尽量避
免将一个类型的字段以公有方式提供给外部。
毕竟,对于字段而言,对象对它的控制非常弱,一旦公开在
外,则调用者可以比较容易的对其进行操作,尤其是写操作,从而可能会导致错误。
例如,我们为前面定
义的User类增加一个age(年龄)字段,假如我将其定义为public字段,如下所示:
publicintAge;
那么调用者可能会将Age的值设为负数:
user.Age=-5;
对于字段的定义而言,并不能判断这样一种不合常理的操作,因为我们对字段的写操作的控制无能为力。
大家可以看到,这里所谓的字段值,其实可以对应于前面所讲的对象的属性。
例如姓名,年龄,就是一个
用户的属性。
如果字段不能设置为public,那么调用者又如何访问它们呢?
答案就是使用C#类中的
property(属性)。
所谓“属性”,很大程度可以看作是对“字段”的一种封装,它利用一种被称为“get/set访问器”分别
控制对字段的读写操作,并暴露一个属性值,如Age属性:
privateintm_age;
publicintAge
{
get{returnm_age;
}
set
if(value<
0)
{
thrownewArgumentOutOfRangeException(”Agemustbegreaterthanorequalto0″);
}
m_age=value;
上面的代码中,throw语句的作用是抛出一个异常,我们暂时可以不去理会它,而是将注意力放到get和
set访问器上。
首先,我们定义了一个私有字段m_age,然后再定义一个公共属性Age。
在该属性中,get返
回私有字段的值m_age,而在set中,首先会判断value的值,如果小于0,那么这个值是非法的,就将抛出
一个异常,停止往下执行,并告诉你对Age值的设置错误了。
当然,我们也可以为value值设置更严格的要
求,例如不允许value大于150。
至少人的年龄现在没有超过150岁的吧。
也许会有人疑问value究竟是什么?
它其实是C#中提供的关键字,代表的就是你赋给该属性的真正的值,
例如:
user.Age=30;
此时是对Age属性赋值,.Net会执行set访问器,而value值就是30。
然后判断30是否小于0。
显然不符合条
件,不会抛出异常,继续执行,将value值30赋给字段m_age。
为什么要赋给m_age呢?
让我们再看看get访
问器,它其实就是一个读操作,返回的值是什么?
对了,就是字段m_age,如下所示:
//set操作,将字段m_age设置为30;
Console.WriteLine(“User’sAgeis{0}.”,user.Age);
//get操作,将m_age的值取出;
此时就会在控制台下显示:
User’sAgeis30.
此外,对于一些特殊的要求,我们在将字段封装为属性时,可以只设置它的get访问器或者set访问器,这
样这个属性就是只读属性,或者只写属性了。
这样显然更有利于对象的封装。
毕竟对于公共字段而言,我
们最能可以控制它为只读(设置为readonly),却无法设置为只写。
从上可以看到,实际上属性就是对字段进行一次封装。
通过这个封装,使我们对字段m_age的读写都具有
了控制功能,至少现在的Age属性能够控制赋值为负数的情况了。
这就是属性的好处。
在C#2.0中,除了可以对整个属性设置public等访问修饰符外,对内部的get/set访问器同样可以设置访
问修饰符,当然它要受到一定的限制。
由于有些限制和接口、重写有关,我暂时不会介绍,在这里,我仅
介绍访问器和属性的访问修饰符冲突问题。
1、如果整个属性被设置为public,则其访问器没有限制;
2、如果整个属性被设置为protectedinternal,则访问器的访问修饰仅能设置为internal,protected或
者private中的一种;
3、如果整个属性被设置为internal或者protected,那么访问器的访问修饰只能是private。
如下例:
publicClassA
privatestringm_text;
privateintm_count;
publicstringText
get{returnm_text;
protectedset{m_text=value;
internalintCount
privateget{return5;
privateset{m_count=value}
从程序的实质来看,其实属性就是一种特殊的方法,它等同于下面的代码:
publicintGetAge()
returnm_age;
publicvoidSetAge(intage)
m_age=age;
从这个意义上来理解get/set访问器的访问级别修饰,就更容易理解了。
实质上,所谓的访问器的访问级
别修饰,不外乎就是对方法进行访问级别修饰罢了。
当然,C#中提供的属性要比访问字段的get/set方法
更加简便。
一般而言,如要定义方法,应该是和一个对象的行为有关,例如系列三定义的User类中的
SignIn()和SignOut()方法,它们代表的是对象User的行为:
登录和退出。
定义一个类的方法,必须包括五个要素:
方法修饰符,方法名,返回类型,参数,以及方法体,例如Add
方法:
publicintAdd(intx,inty)
returnx+y;
public即为我们的方法修饰符,它代表了该方法能被访问的级别。
当然,修饰的方法的关键字还包括
static,virtual,abstract等,不过这些内容会在以后介绍。
方法名自然是Add了,自然属于方法的名字
。
返回类型为int,代表该方法会返回一个结果,该结果类型为int类型。
参数有两个,分别为x和y,它们
的类型都是int。
调用者可以通过参数传递值到方法体中,并对它们进行操作。
方法体则是花括号中的内
容。
假设Add方法是定义在类Calculator中,那么该方法的调用为:
Calculatorcal=newCalculator();
intresult=cal.Add(3,5);
通过对Add的调用,并传入3和5的参数,最后得到结果8,并返回。
因此,此时变量result的值就为8。
而
第一行代码,则是利用new关键字对Calculator类进行实例化,获得一个对象cal。
通过对象cal,才可以
调用Calculator类的公共方法、属性或字段。
为什么要进行实例化呢?
我们定义一个类类型,是为调用者所使用的,否则就失去其意义了。
但我们定义
的这样一个类类型,仅仅是代表了某种格式而已,例如User类说明它是一个class,它拥有了一些字段、
属性和方法。
通过这样的定义,我们在使用这些类型的对象时,.Net能够识别它。
而如果真正要调用这些
类型对象,就必须进行”实例化”,这个操作就会在运行期间,创建一个个对象,并被放在内存空间中供
程序调用。
就好比”人”就是一个类类型,而某一个具体的人,才是被实例化的、真正存在的对象。
要使
得一个类类型被实例化,就需要为该类型提供”构造器”。
构造器是一种特殊的方法,它没有返回类型,
且其方法名和类型名保持一致,如Calculator类的定义以及它的构造器:
publicclassCalculator
publicCalculator()
returnx+y;
Calculator()方法就是一个”构造器”,这个构造器并没有参数,在C#中,也被称为默认的构造器,即使
不定义该构造器,.Net也会为它默认创建。
例如在Calculator类中,我们完全可以删去Calculator()构造
器的定义。
然而,一旦我们定义了有参数的构造器时,则该默认构造器将不存在,如果我们再需要不带参
数创建实例的话,就需要显式创建该构造器了。
例如之前的User类。
如果姓名和密码是该类一个非常重要
的属性,大部分情况下,如果要创建User对象时,都需要这两个属性的值时,我们就可以为User类专门创
建一个构造器:
publicclassUser
publicUser(stringname,stringpassword)
m_name=name;
m_password=password;
}
注意在这个构造器中,接收两个参数name和password,并将其值赋给User类的字段m_name,m_password。
因此,当我们通过如下的方式创建User类的实例时,我们创建的对象就已经具有Name和Password的值了:
UserspecUser=newUser(”brucezhang”,“password”);
然而此时如果利用下面的方式创建User的实例,就会出现错误:
Useruser=newUser();
因为此时User类的默认构造器(即无参的构造器)已经不存在,如要支持上面的实例化方式,就需要在
User类的定义中添加无参构造器。
是否需要为一个类定义有参的构造器,应根据具体的需要而定。
以User类而言,由于该类的Name和
Password属性是一个对象必备的,那么建立这样一个构造器是有必要的。
因为如果不具备这样的构造器,
那么如前构造的specUser就需要用下面三行代码来完成:
UserspecUser=newUser();
specUser.Name=“brucezhang”;
specUser.Password=“password”;
注意,在一个类的定义中,我们可以使用this关键字来代表其类对象本身,通过this,可以访问到这个类
的所有常量、字段、属性和方法,不管它们是public还是private,或者其他访问级别。
虽然这个this指
代的是类对象本身,也就是说它代表的就是实例化所产生的对象,但this的含义也仅仅限于对象的内部,
从对象封装的思想来看,它是被封装了的,在对象外部是无法看到的。
例如下面的定义:
publicclassVistor
publicvoidVisit(Elemente)
Console.WriteLine(”Iwasvisited.”);
publicclassElement
publicvoidAccept(Visitorv)
v.Visit(this);
在Element类中,Accept方法则传入一个Visitor类型的参数值,在该方法中,调用参数v的方法Visit,而
Visit方法传入的是Element类型的值,由于Accept方法本身就属于Element类,因此,我们可以把其自身
传递到Visit方法中,也就是代码中的this。
分析如下的代码段:
Vistorv=newVistor();
Elemente=newElement();
e.Accept(v);
Element的实例e,执行了Accept()方法,该方法传入的参数是Visitor类的实例v。
那么执行Accept方法,
实质就是在其方法内部执行v.Visitor()方法,并通过this关键字将对象e本身传入,所以最后的结果是,
打印如下的字符串:
Iwasvisited。
这这里顺便提一下命名的要求。
所谓命名规范,在作为团队开发的时候,是非常重要的。
以本文为例,如
何定义类名、字段名、属性名和方法名,都是有讲究的。
通常来说,类名、属性名和方法名都要求所有单
词的首字母大写。
如果是字段,那么除非是公共字段,一般而言,应将第一个单词的首字母小写。
不过这
也是变量命名的要求。
由于在一个类中,可能会临时用到一些变量,而不是字段,为了区别一般变量和字
段,C++的程序员喜欢在变量名前加上“_”符号,许多C#程序员也沿用了这个习惯。
不过我更喜欢为这些
字段名前加上“m_”。
命名一定要统一,尤其是在一个团队中,不过类似于这些临时变量,或者非公有变
量,对名字的限制要少一些,毕竟这些变量不会被类的调用者使用。
此外,对于常量而言,最好定义为全
部大写的名字,如前面的定义的常量PI。
C#专门有一套完整的命名规范,有兴趣的可以自己去查阅一下专门的资料。
此外,不同的公司可能还有一
些特定的命名规范,在这里就不再赘述了。
2Comments»
前几天没时间,今天晚上看完了你写的四,终于能够透彻理解属性了.不过,对于this我还是模模糊糊,感觉
没看明白.虽然知道this就是指的是本类的对象,但是,当一个类创建了好几个对象后,
是不是就不能用this了,如果能用,它代表哪个?
再就是this是不是就是代表它所在类的一个通用的对象?
不
然,为什么一个类没创建对象,仍然可以用this,你举的例子里面就是.
(感觉好像有点像方法里的形参作用一样).在你举的例子中后来创建了一个对象,那this就代表这个对象,
如果创建了2个对象那,那它代表哪个?
Commentby不染丹心—May1,2006@11:
18pm
@不染丹心:
我说了,“this的含义也仅仅限于对象的内部”。
因此,对于实际实例化的对象而言,你不能直接使用
this。
实际上,在对象内部,this就代表对象实例本身。
假设类的方法Foo()中使用了this。
当你创建了
一个实例a,并调用方法Foo,此时this就指代实例a。
如果你同时又创建了实例b,也调用方法Foo,此时
this就指代实例b。