1 类和结构
类和结构可以看作是创建对象的模板。每个对象都包括数据,并提供处理和访问数据的方法。类定义了每个对象(实例)包含什么样的数据与功能
1.1
“封装”有时被称为面向对象的编程的第一个支柱或原则。根据封装的原则,类或结构可以指定其每个成员对于该类或结构外部的代码的可访问性。可将无意在类或程序集外部使用的方法和变量隐藏起来,以减小编码错误或遭恶意利用的可能性。
1.2 成员
所有方法、字段、常量、属性和事件都必须在类型内部进行声明;这些称为类或结构的“成员”。与其他一些语言不同的是,C# 中没有全局变量或方法。即使是作为程序入口点的 Main 方法也必须在类或结构内部进行声明。下表列出了可在类或结构中声明的所有不同种类的成员。
2 类与结构的区别
2.1值类型与引用类型
结构是值类型:值类型在堆栈上分配地址,所有的基类型都是结构类型,例如:int 对应System.int32 结构,string 对应 system.string 结构 ,通过使用结构可以创 建更多的值类型
类是引用类型:引用类型在堆上分配地址
堆栈的执行效率要比堆的执行效率高,可是堆栈的资源有限,不适合处理大的逻辑复杂的对象。所以结构处理作为基类型对待的小对象,而类处理某个商业逻辑
因为结构是值类型所以结构之间的赋值可以创建新的结构,而类是引用类型,类之间的赋值只是复制引用
注:
1.虽然结构与类的类型不一样,可是他们的基类型都是对象(object),c#中所有类型的基类型都是object
2.虽然结构的初始化也使用了New 操作符可是结构对象依然分配在堆栈上而不是堆上,如果不使用“新建”(new),那么在初始化所有字段之前,字段将保持未赋值状态,且对象不可用
2.2 继承性
结构:不能从另外一个结构或者类继承,本身也不能被继承,虽然结构没有明确的用sealed声明,可是结构是隐式的sealed .
类:完全可扩展的,除非显示的声明sealed 否则类可以继承其他类和接口,自身也能被继承
注:虽然结构不能被继承 可是结构能够继承接口,方法和类继承接口一样
2.3内部结构:
结构:
没有默认的构造函数,但是可以添加构造函数
没有析构函数
没有 abstract 和 sealed(因为不能继承)
不能有protected 修饰符
可以不使用new 初始化
在结构中初始化实例字段是错误的
类:
有默认的构造函数
有析构函数
可以使用 abstract 和 sealed
有protected 修饰符
必须使用new 初始化
3 如何选择结构还是类
讨论了结构与类的相同之处和差别之后,下面讨论如何选择使用结构还是类:
1. 堆栈的空间有限,对于大量的逻辑的对象,创建类要比创建结构好一些
2. 结构表示如点、矩形和颜色这样的轻量对象,例如,如果声明一个含有 1000 个点对象的数组,则将为引用每个对象分配附加的内存。在此情况下,结构的成本较低。
3. 在表现抽象和多级别的对象层次时,类是最好的选择
4. 大多数情况下该类型只是一些数据时,结构时最佳的选择
4 类成员
类中的数据和函数称为类的成员。类的主要成员包括两种类型,即描述状态的数据成员和描述操作的函数成员。
4.1 数据成员
4.1.1 字段
字段就是在类内定义的成员变量,用来存储描述类的特征的值
类或结构可以拥有实例字段或静态字段,或同时拥有两者。实例字段特定于类型的实例。如果您拥有类 T 和实例字段 F,可以创建类型 T 的两个对象,并修改每个对象中 F 的值,这不影响另一对象中的该值。相比之下,静态字段属于类本身,在该类的所有实例中共享。从实例 A 所做的更改将立刻呈现在实例 B 和 C 上(如果它们访问该字段)。
字段可标记为 public、private、protected、internal 或 protected internal
注意:通常应仅为具有私有或受保护可访问性的变量使用字段。您的类向客户端代码公开的数据应通过方法、属性和索引器提供。通过使用这些构造间接访问内部字段,可以针对无效的输入值提供防护。
4.1.2 常量
常量是在编译时已知并在程序的生存期内不发生更改的不可变值。常量使用 const 修饰符进行声明。只有 C# 内置类型(System.Object 除外)可以声明为 const
用户定义的类型(包括类、结构和数组)不能为 const。应使用 readonly 修饰符创建在运行时初始化一次即不可再更改的类、结构或数组。
常量可标记为 public、private、protected、internal 或 protectedinternal。这些访问修饰符定义类的用户访问该常量的方式。
因为常量值对该类型的所有实例是相同的,所以常量被当作 static 字段一样访问。不使用 static 关键字声明常量。未包含在定义常量的类中的表达式必须使用类名、一个句点和常量名来访问该常量
4.2 函数成员
函数成员提供了操作类中数据的功能,包括方法,属性,构造函数与终结器,运算符以及索引器。
方法:方法是与某个类相关的函数。它可以是实例方法也可以静态方法。
属性:属性是这样的成员:它们提供灵活的机制来读取、编写或计算私有字段的值。可以像使用公共数据成员一样使用属性,但实际上它们是称作“访问器”的特殊方法。这使得可以轻松访问数据,此外还有助于提高方法的安全性和灵活性。
构造函数:任何时候,只要创建类或结构,就会调用它的构造函数。类或结构可能有多个接受不同参数的构造函数。构造函数使得程序员可设置默认值、限制实例化以及编写灵活且便于阅读的代码。
如果没有为对象提供构造函数,则默认情况下 C# 将创建一个构造函数,该构造函数实例化对象,并将成员变量设置为下表中列出的默认值。静态类和结构也可以有构造函数。
在 C# 中不允许使用未初始化的变量。
下例显示了由默认构造函数返回的值类型的默认值。默认构造函数是通过 new 运算符来调用的
int myInt = new int();
以上语句同下列语句效果相同:
int myInt = 0;
析构函数 析构函数用于析构类的实例。
- 不能在结构中定义析构函数。只能对类使用析构函数。
- 一个类只能有一个析构函数。
- 无法继承或重载析构函数。
- 无法调用析构函数。它们是被自动调用的。
- 析构函数既没有修饰符,也没有参数。
程序员无法控制何时调用析构函数,因为这是由垃圾回收器决定的。垃圾回收器检查是否存在应用程序不再使用的对象。如果垃圾回收器认为某个对象符合析构,则调用析构函数(如果有)并回收用来存储此对象的内存。程序退出时也会调用析构函数。
运算符
C# 中,“运算符”是一个术语或符号,它接受一个或多个表达式(即“操作数”)作为输入并返回值。接受一个操作数的运算符称为“一元”运算符,例如增量运算符 (++) 或 new。接受两个操作数的运算符称为“二元”运算符,例如算术运算符 +、-、*、/。条件运算符 ?: 接受三个操作数,是 C# 中唯一的三元运算符。
索引器
索引器允许类或结构的实例就像数组一样进行索引。索引器类似于属性,不同之处在于它们的访问器采用参数。
4.2.1 方法
通过指定方法的访问级别(例如 public 或 private)、可选修饰符(例如 abstract 或 sealed)、返回值、名称和任何方法参数,可以在类或结构中声明方法。这些部分统称为方法的“签名”。
[modifiers] retunr_type MehodName({paramaters})
{
// method body
}
方法重载
方法重载是让类以统一的方式处理不同类型数据的一种手段。在C#中,语法规定同一个类中两个或两个以上的方法可以用同一个名字,如果出现这种情况,那么该方法就被称为重载方法.
当一个重载方法被调用时,C#回根据调用该方法的参数自动调用具体的方法来执行.对于方法的使用者来讲,这种技术是非常有用的。
决定方法是否构成重载有以下几个条件:
◆ 在同一个类中;
◆ 方法名相同;
◆ 参数列表不同。
◆两个方法不能仅在返回类型上有区别
◆两个方法不能仅根据参数是否声明为ref或out来区分
属性(property)
属性是对现实世界中实体特征的抽象,提供了对类或对象性质的访问。类的属性所描述的是状态信息,在类的某个实例中,属性的值表示该对象的状态值。
属性是字段的自然扩展,它们都是具有关联类型的命名成员,而且访问字段和属性的语法是相同的。然而,与字段不同,属性不表示存储位置。
属性有访问器(accessor),这些访问器指定在它们的值被读取或写入时需执行的语句。因此属性提供了一种机制,它把读取和写入对象的某些性质与一些操作关联起来。
它们甚至还可以对此类性质进行计算。因此,属性被称为聪明的字段。
属性(property)
-充分体现了对象的封装性:不直接操作类的数据内容,而是通过访问器进行访问,即借助于get和set对属性的值进行读写;另一方面还可以对数据的访问属性进行控制(当然也可以通过对普通域加readonly关键字来实现。
-设计原则:属性封装了对域的操作。把要访问的域设为private,通过属性中的get和set操作对域进行设置或访问。
-不能把属性作为引用类型或输出参数来进行传递。
-get方法没有参数;set方法有一个隐含的参数value。除了使用了abstract修饰符的抽象属性,每个访问器的执行体中只有分号“;”外,其他的所有属性的get访问器都通过return来读取属性的值,set访问器都通过value来设置属性的值。
-采用间接方式来访问对象的属性(间接调用get、set方法):对象.属性 = 值(调用set),变量 = 对象.属性(调用get)。
-在属性的访问声明中:
只有set访问器,表明该属性是只写的。
只有get访问器,表明该属性是只读的。
既有set访问器,又有get访问器,表明该属性是可读可写的。
属性的声明形式如下:
『特性』
『修饰符』【类型】 【属性名】
{
『get { 【get访问器体】 }』
『set { 【set访问器体】 }』
}
特性』和『修饰符』都是可选的,适用于字段和方法的所有特性和修饰符都适用于属性,最常用的就是访问修饰符。也可以使用static修饰符。当属性声明包含static修饰符时,
称该属性为静态属性(static property)。当不存在static修饰符时,称该属性为实例属性(instance property)。
静态属性不与特定实例相关联,因此在静态属性的访问器内引用this会导致编译时错误。
实例属性与类的一个给定实例相关联,并且可以在属性的访问器内通过this来访问该实例。
类的属性称为智能字段,类的索引器称为智能数组。由于类本身作数组使用,所以用
this作索引器的名称,索引器有索引参数值。例:
using System;
using System.Collections;
class MyListBox
{
protected ArrayList data = new ArrayList();
public object this[int idx] //this作索引器名称,idx是索引参数
{
get
{
if (idx > -1 && idx < data.Count)
{
return data[idx];
}
else
{
return null;
}
}
set
{
if (idx > -1 && idx < data.Count)
{
data[idx] = value;
}
else if (idx = data.Count)
{
data.Add(value);
}
else
{
//抛出一个异常
}
}
}
}
}
自动实现的属性
如果属性的get和set访问器中没有任何逻辑,就可以使用自动实现的属性。这种属性会自动实现基础成员变量
public string Name
{
get
{
return _name
}
set
{
_name=value
}
}
这段代码和下面这行代码是一样的
public string Nmae{get;set;}
不需要声明private string _name. 但自动实现的属性必须有两个访问器
构造函数
构造函数可以初始类,函数重载可以实现多态。
构造函数和析构函数是类的特殊方法,分别用来初始化类的实例和销毁类的实例。
构造函数(constructor)用于执行类的实例的初始化。每个类都有构造函数。即使没有为类声明构造函数,编译器也会自动地为类提供一个默认的构造函数。
构造函数的名称与类名相同,其基本特征为:
— 构造函数不声明返回类型(甚至也不能使用void),也不能返回值。
— 一般地,构造函数总是public 类型的。private 类型的构造函数表明类不能被实例化,通常用于只含有静态成员的类。
— 在构造函数中不要做对类的实例进行初始化以外的事情,也不能被显式地调用。
下面的代码说明了构造函数的声明方式:
// Dog.cs
// 构造对象时将执行构造函数
using System;
public class Dog
{
public string name;
public Dog() //声明构造函数
{
name ="未知";
Console.WriteLine("Dog():Dog类已被初始化。");
}
public void Bark()
{
Console.WriteLine("汪汪!");
}
public static void Main()
{
DogmyDog = new Dog(); // 会调用构造函数
Console.WriteLine("myDog的名字为“{0}”,叫声为:",myDog.name);
myDog.Bark();
}
}
在访问一个类的时候,系统将首先执行构造函数中的语句。构造函数的功能是创建对象,使对象的状态合法化。
在从构造函数返回之前,对象都是不确定的,不能用于执行任何操作;只有在构造函数执行完成之后,存放对象的内存块中才存放这一个类的实例。
使用构造函数
构造函数是在创建给定类型的对象时执行的类方法。构造函数具有与类相同的名称,它通常初始化新对象的数据成员。
在下面的示例中,定义了一个具有一个简单的构造函数,名为 Taxi 的类。然后使用 new 运算符来实例化该类。在为新对象分配内存之后,new 运算符立即调用 Taxi 构造函数。
public class Taxi
{
public bool isInitialized;
public Taxi()
{
isInitialized = true;
}
}
class TestTaxi
{
static void Main()
{
Taxi t = new Taxi();
System.Console.WriteLine(t.isInitialized);
}
}
不带参数的构造函数称为“默认构造函数”。无论何时,只要使用 new 运算符实例化对象,并且不为 new 提供任何参数,就会调用默认构造函数
除非类是 static 的,否则 C# 编译器将为无构造函数的类提供一个公共的默认构造函数,以便该类可以实例化。
实例构造函数
实例构造函数用于创建和初始化实例。创建新对象时将调用类构造函数
class CoOrds
{
public int x, y;
// constructor
public CoOrds()
{
x = 0;
y = 0;
}
}
无论何时创建基于 CoOrds 类的对象,都会调用此构造函数。诸如此类不带参数的构造函数称为“默认构造函数”。然而,提供其他构造函数通常十分有用。
例如,可以向 CoOrds 类添加构造函数,以便可以为数据成员指定初始值:
// A constructor with two arguments:
public CoOrds(int x, int y)
{
this.x = x;
this.y = y;
}
这样便可以用默认或特定的初始值创建 CoOrd 对象,如下所示:
CoOrds p1 = new CoOrds();
CoOrds p2 = new CoOrds(5, 3);
如果类没有默认构造函数,将自动生成一个构造函数,并且将用默认值来初始化对象字段
私有构造函数
私有构造函数是一种特殊的实例构造函数。它通常用在只包含静态成员的类中。如果类具有一个或多个私有构造函数而没有公共构造函数,
则不允许其他类(除了嵌套类)创建该类的实例。例如:
lass NLog
{
// Private Constructor:
private NLog() { }
public static double e = System.Math.E; //2.71828...
}
声明空构造函数可阻止自动生成默认构造函数。注意,如果您不对构造函数使用访问修饰符,则在默认情况下它仍为私有构造函数。
但是,通常显式地使用 private 修饰符来清楚地表明该类不能被实例化。
当没有实例字段或实例方法(如 Math 类)时或者当调用方法以获得类的实例时,私有构造函数可用于阻止创建类的实例。
如果类中的所有方法都是静态的,可考虑使整个类成为静态的。
静态构造函数
静态构造函数用于初始化任何静态数据,或用于执行仅需执行一次的特定操作。在创建第一个实例或引用任何静态成员之前,将自动调用静态构造函数
class SimpleClass
{
// Static constructor
static SimpleClass()
{
//...
}
}
静态构造函数具有以下特点:
示例:类
Bus 有一个静态构造函数和一个静态成员
Drive()。当调用
Drive() 时,将调用静态构造函数来初始化类。
public class Bus
{
// Static constructor:
static Bus()
{
System.Console.WriteLine("The static constructor invoked.");
}
public static void Drive()
{
System.Console.WriteLine("The Drive method invoked.");
}
}
class TestBus
{
static void Main()
{
Bus.Drive();
}
}
posted on 2011-10-09 21:58
Java_liyadong 阅读(357)
评论(0) 编辑 收藏