C#语言系列讲座(7) 域与属性

● 域(Field)又称成员变量(Member Variable),分为实例域和静态域。域的存取限制集中体现了面向对象编程的封装原则;

● 属性的背后是两个函数:赋值函数(get)和取值函数(set)。



域(Field)又称成员变量(Member Variable),它表示存储位置,是C#类中不可缺少的一部分。域的类型可以是C#中任何数据类型,但对于除string类型的其他引用类型,由于在初始化时涉及到一些类的构造器的操作,计划放在以后的章节里讲述。

域分为实例域和静态域。实例域属于具体的对象,为特定的对象所专有。静态域属于类,为所有对象所共用。C#严格规定实例域只能通过对象来获取,静态域只能通过类来获取。

域的存取限制集中体现了面向对象编程的封装原则。如前所述,C#中的存取限制修饰符有5种,这5种对域都适用。C#只是用internal扩展了C++原来的friend修饰符。在有必要使两个类的某些域互相可见时,将这些类的域声明为internal,然后将它们放在一个组合体内编译即可。如果需要对它们的继承子类也可见的话,声明为protected internal即可。

C#引入了readonly修饰符来表示只读域,引入const来表示不变常量。顾名思义对只读域不能进行写操作,不变常量不能被修改,这两者到底有什么区别呢?只读域只能在初始化(声明初始化或构造器初始化)的过程中赋值,否则编译器会报错。只读域可以是实例域也可以是静态域。只读域的类型可以是C#语言的任何类型。而const修饰的常量必须在声明的同时赋值,而且要求编译器能够在编译时计算出这个确定的值。const修饰的常量为静态变量,不能够为对象所获取。const修饰的值的类型也有限制,它只能为下列类型之一(或能够转换为下列类型的类型):sbyte, byte, short, ushort, int, uint, long, ulong, char, float, double, decimal, bool, string, enum类型, 或引用类型。值得注意的是这里的引用类型,由于除去string类型外,所有的类型(除值为null外)在编译时都不能由编译器计算出它们的确切的值,所以能够声明为const的引用类型只能为string或值为null的其他引用类型。如果需要一个const的常量,但它的类型又限制了它不能在编译时被计算出确定的值来,此时可采取将它声明为static readonly来解决。const修饰的常量是在编译时被计算出确定的值,并代换到引用该常量的每一个地方,而readonly是在运行时才确定值,不希望它的值再改变,这是两者的主要区别。

域的初始化是面向对象编程中一个需要特别注意的问题。C#编译器缺省将每一个域初始化为它的默认值。简单地说,数值类型(枚举类型)的默认值为0或0.0,字符类型的默认值为‘/x0000’,布尔类型的默认值为false,引用类型的默认值为null,结构类型的默认值为其内的所有类型都取其相应的默认值。虽然C#编译器为每个类型都设置了默认类型,但按照面向对象的设计原则,还是需要对变量进行正确的初始化,这也是C#推荐的做法,没有对域进行初始化会导致编译器发出警告信息。C#中对域进行初始化有两个地方:声明的同时进行初始化和在构造器内进行初始化。如前所述,域的声明初始化实际上被编译器作为赋值语句放在了构造器的内部的最开始处执行。实例变量初始化会被放在实例构造器内,静态变量初始化会被放在静态构造器内。如果声明了一个静态的变量并同时对之进行了初始化,那么编译器将构造出一个静态构造器来把这个初始化语句变成赋值语句放在里面。而作为const修饰的常量域,从严格意义上讲不能算做初始化语句,可以将它看做类似于C++中的宏代换。

属性

属性可以说是C#语言的一个创新,当然也可以说不是。不是的原因是它背后的实现实际上还是两个函数:赋值函数(get)和取值函数(set),这从它生成的中间语言代码可以清晰地看到。是的原因是它的的确确在语言层面实现了面向对象编程一直以来对“属性”这一OO风格的类的特殊接口的需求。C#不提倡将域的保护级别设为public,而使用户在类外任意操作——那样太不安全!对所有有必要在类外可见的域,C#推荐采用属性来表达。属性不表示存储位置,这是属性和域的根本性的区别。下面是一个典型的属性设计:

using System;

class MyClass {

int integer;

public int Integer {

get {return integer;}

set {integer=value;}

}

}

class Test {

public static void Main() {

MyClass MyObject=new MyClass();

Console.Write(MyObject.Integer);

MyObject.Integer++;

Console.Write(MyObject.Integer);

}

}

正如期待的那样,程序输出“0 1”。可以看到属性通过对方法的包装给程序员提供了一个友好的域成员的存取界面。这里的value是C#的关键字,是进行属性操作时set函数的隐含参数,也就是在执行属性写操作时的右值。

属性提供了只读(get),只写(set),读写(get和 set)三种接口操作。对域的这三种操作,必须在同一个属性名下声明,而不可以将它们分离。

当然属性不止仅仅限于域的接口操作,属性的本质还是方法,可以根据程序逻辑在属性的提取或赋值时进行某些检查、警告等额外操作,看下面的例子:

class MyClass {

private string name;

public string Name {

get { return name; }

set {

if (value==null)

name=“Microsoft”;

else

name=value;

}

}

}

由于属性的方法本质,属性当然也有方法的种种修饰,属性也有5种存取修饰符,但属性的存取修饰往往为public,否则也就失去了属性作为类的公共接口的意义。除了方法的多参数带来的方法重载等特殊属性不具备外, virtual、 sealed、 override、 abstract等修饰符对属性有与方法同样的行为,但由于属性在本质上被实现为两个方法,它的某些行为需要注意。看下面的例子:

abstract class A {

int y;

public virtual int X {

get { return 0; }

}

public virtual int Y {

get { return y; }

set { y = value; }

}

public abstract int Z { get; set; }

}

class B: A {

int z;

public override int X {

get { return base.X + 1; }

}

public override int Y {

set { base.Y = value < 0? 0: value; }

}

public override int Z {

get { return z; }

set { z = value; }

}

}

这个例子集中地展示了属性在继承上下文中的某些典型行为。这里,类A由于抽象属性Z的存在而必须声明为abstract。子类B中通过base关键字来引用父类A的属性。类B中可以只通过Y的set函数便覆盖了类A中的虚属性。

静态属性和静态方法一样只能存取类的静态域变量。如同外部方法那样,可以声明外部属性。
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值