类(class)是C#类型中最基础的类型。类是一个数据结构,将状态(字段)和行为(方法和其他函数成员)组合在一个单元中。类提供了用于动态创建类实例的定义,也就是对象(object)。类支持继承(inheritance)和多态(polymorphism),即派生类能够扩展和特殊化基类的机制。 使用类声明可以创建新的类。类声明以一个声明头开始,其组成方式如下:先是指定类的特性和修饰符,后跟类的名字,基类(如果有的话)的名字,以及被该类实现的接口名。声明头后面就是类体了,它由一组包含在大括号({})中的成员声明组成。 下面是一个名为Point的简单类的声明: public class Point { public int x, y;
public Point(int x, int y){ this.x = x; this.y = y; } } 使用new运算符创建类的实例,它将为新实例分配内存,调用构造函数初始化实例,并且返回对该实例的引用。下面的语句创建两个Point对象,并且将那些对象的引用保存到两个变量中: Point p1 = new Point(0, 0); Point p2 = new Point(10, 20); 当不再使用对象时,该对象所占的内存将被自动回收。在C#中,没有必要也不可能显式地释放对象。 1.成员分类: (1)时间: 类的成员或者是静态成员(static member)----属于类。 实例成员(instance member)----属于对象。 (2)空间: 属性。 方法。 索引。 代理。 事件。 嵌套类。 字段。 2.成员的可访问性: 类的每个成员都有关联的可访问性,它控制能够访问该成员的程序文本区域。 public :访问不受限制。
protected :访问仅限于包含类或从包含类派生的类型。
internal :访问仅限于当前程序集。
protected internal :访问仅限于从包含类派生的当前程序集或类型。
private :访问仅限于包含类。
3.成员的修饰符:
abstract:指示该方法或属性没有实现。
sealed:密封方法。可以防止在派生类中对该方法的override(重载)。不是类的每个成员方法都可以作为密封方法密封方法,必须对基类的虚方法进行重载,提供具体的实现方法。所以,在方法的声明中,sealed修饰符总是和override修饰符同时使用。
delegate:委托。用来定义一个函数指针。C#中的事件驱动是基于delegate + event的。
const:指定该成员的值只读不允许修改。
event:声明一个事件。
extern:指示方法在外部实现。
override:重写。对由基类继承成员的新实现。
readonly:指示一个域只能在声明时以及相同类的内部被赋值。
static:指示一个成员属于类型本身,而不是属于特定的对象。即在定义后可不经实例化,就可使用。
virtual:指示一个方法或存取器的实现可以在继承类中被覆盖。
new:在派生类中隐藏指定的基类成员,从而实现重写的功能。 若要隐藏继承类的成员,请使用相同名称在派生类中声明该成员,并用 new 修饰符修饰它。
4.方法 方法(method)是一种用于实现可以由对象或类执行的计算或操作的成员。静态方法(static method)只能通过类来访问。实例方法(instance method)则要通过类的实例访问。 (1).方法的参数 参数用于将值或者引用变量传递给方法。当方法被调用时,方法的参数译注5从指定的自变量(argument)译注6得到它们实际的值。C#有4种参数:值参数、引用参数、输出参数和参数数组。 值参数(value parameter)用于输入参数的传递。值参数相当于一个局部变量,它的初始值是从为该参数所传递的自变量获得的。对值参数的修改不会影响所传递的自变量。 引用参数(reference parameter)用于输入和输出参数的传递。用于引用参数的自变量必须是一个变量,并且在方法执行期间,引用参数和作为自变量的变量所表示的是同一个存储位置。引用参数用ref修饰符声明。下面的示例展示了ref参数的使用: using System;
class Test
{
static void Swap(ref int x, ref int y) {
int temp = x;
x = y;
y = temp;
}
static void Main() {
int i = 1, j = 2;
Swap(ref i, ref j);
Console.WriteLine("{0} {1}", i, j); //输出 "2 1"
}
} 输出参数(output parameter)用于输出参数的传递。输出参数类似于引用参数,不同之处在于调用方提供的自变量初始值无关紧要。输出参数用out修饰符声明。下面的示例展示了out参数的使用: using System;
class Test { static void Divide(int x, int y, out int result, out int remainder) { result = x / y; remainder = x % y; } static void Main() { int res, rem; Divide(10, 3, out res, out rem); Console.WriteLine("{0} {1}", res, rem); //输出 "3 1" } } 参数数组(parameter array)允许将可变长度的自变量列表传递给方法。参数数组用params修饰符声明。只有方法的最后一个参数能够被声明为参数数组,而且它必须是一维数组类型。System.Console类的Write和WriteLine方法是参数数组应用的很好的例子。它们的声明形式如下: public class Console { public static void Write(string fmt, params object[] args) {...}
public static void WriteLine(string fmt, params object[] args) {...} ... } (2)虚拟方法、重写方法和抽象方法 若一个实例方法的声明中含有virtual修饰符,则称该方法为虚拟方法(virtual method)。若其中没有virtual修饰符,则称该方法为非虚拟方法(nonvirtual method)。 在一个虚拟方法调用中,该调用所涉及的实例的运行时类型(runtime type)确定了要被调用的究竟是该方法的哪一个实现。在非虚拟方法调用中,实例的编译时类型(compile-time type)是决定性因素。 虚拟方法可以由派生类重写(override)译注7实现。当一个实例方法声明中含有override修饰符时,该方法将重写所继承的相同签名的虚拟方法。虚拟方法声明用于引入新方法,而重写方法声明则用于使现有的继承虚拟方法专用化(通过提供该方法的新实现)。 抽象(abstract)方法是没有实现的虚拟方法。抽象方法的声明是通过abstract修饰符实现的,并且只允许在抽象类中使用抽象方法声明。非抽象类的派生类需要重写抽象方法。 (3)方法重载 方法重载(Method overloading)允许在同一个类中采用同一个名称声明多个方法,条件是它们的签名是惟一的。当编译一个重载方法的调用时,编译器采用重载决策(overload resolution)确定应调用的方法。重载决策找到最佳匹配自变量的方法,或者在没有找到最佳匹配的方法时报告错误信息。下面的示例展示了重载决策工作机制。
5.变量(C#中类的变量称为字段。类的public变量称为类的公共字段) C#要求局部变量在其值被获得之前明确赋值(definitely)。例如,假设前面的变量i的声明没有包含初始值,那么,在接下来对i的使用将导致编译器报告错误,原因就是i在程序中没有明确赋值。
6.事件 事件是使对象或类能够提供通知的成员。事件的声明与字段的类似,不同之处在于事件声明包含一个event关键字,并且事件声明的类型必须是委托类型。 在包含事件声明的类中,事件可以像委托类型的字段一样使用(这样的事件不能是 abstract,而且不能声明访问器)。该字段保存了一个委托的引用,表示事件处理程序已经被添加到事件上。如果尚未添加任何事件处理程序,则该字段为null。 List类声明了名为Changed的单个事件成员,Changed事件表明有一个新项添加到事件处理程序列表,它由OnChanged虚拟方法引发,它首先检查事件是否为null(意思是没有事件处理程序)。引发事件的通知正好等价于调用事件所表示的委托——因此,不需要特殊的语言构件引发事件。 客户通过事件处理程序(event handler)响应事件。使用“+=”运算符添加或者使用“-=”移除事件处理程序。下面的示例添加一个事件处理程序到List类的Changed事件: using System;
class Test { static int changeCount; static void ListChanged(object sender, EventArgs e) { changCount++; } static void Main() { List names = new List(); names.Changed += new EventHandler(ListChanged); names.Add("Liz"); names.Add("Martha"); names.Add("Beth"); Console.WriteLine(changeCount); //输出 "3" } } 7. 析构函数 析构函数(destructor)是用于实现析构类实例所需操作的成员。析构函数不能带参数,不能具有可访问性修饰符,也不能被显式地调用。垃圾回收期间会自动调用所涉及实例的析构函数。 在类的继承中,类的析构函数是不会被继承的。
垃圾回收器在决定何时回收对象和运行析构函数方面采取宽松的策略。特别指出,析构函数的调用时机是不确定的,并且析构函数可能运行在任何线程上。由于这些或者其他原因,只有没有其他可行的解决方案,类才实现析构函数。
8.构造方法(constructor) 构造函数分为实例构造函数,静态构造函数。 (1)实例构造函数:用于实例成员的初始化,可以访问静态成员和实例成员。 如: public Class1 { ...... } (2)静态构造函数:用于静态成员的初始化,只可以访问静态成员。类的静态成员属于类所有,不必生成实例就可以访问。它是在载入包含类的应用程序时创建的,通常,静态变量是在定义时就赋初始值的。 如: public static Class1() { ...... }
9.常量:其值是在编译时设定的,必须是数值文字。默认状态下常量是静态的(关健字:const)。 如: public class Class1 { public const double pi = 3.1415; }
10.属性 类的属性由一个protected(也可以是private)字段和get和set方法构成: public class Class1 { private string ip;//字段 { get { return ip; } set { ip=value; } };
} 只读属性是指省略set方法的属性,只读属性只能读取,不能设置。 属性也可以用限定符virtual,override和abstract修饰,功能同其他类的方法。 属性有一个用处称为懒惰的初始化(lazy initialization)。即在需要类成员时才对它们进行 初始化。如果类中包含了很少被引用的成员,而这些成员的初始化又会花费大量的时候和系统 资源的话,懒惰的初始化就很有用了。
|
请发表评论