C#对象和类型——C#高级编程第三章

  本节内容:类和结构的区别、类成员、按值和按引用传送参数、方法重载、构造函数和静态构造函数、只读字段、部分类、静态类、Object类(基类)。

一、类和结构

类用class修饰,结构用struct修饰。如:

 1 class PhoneCustomer
2 {
3 public const string DayOfSendingBill="Monday";
4 public int CustomerID;
5 public string FirstName;
6 public string LastName;
7 }
8
9 struct PhoneCustomerStruct
10 {
11 public const string DayOfSendingBill="Monday";
12 public int CustomerID;
13 public string FirstName;
14 public string LastName;
15 }

  结构和类的区别是它们在内存中的存储方式、访问方式(类是存储在堆上的引用类型,而结构是存储在栈上的值类型)和它们的一些特征(如结构不支持继承)。较小的数据类型使用结构可以提高性能。在语法上,两者十分相似。对于类和结构都使用new来声明实例:这个关键字创建对象并对其进行初使化。

二、类:类中的数据和函数称为类的成员

1.数据成员:是包含类的数据——字段、常量、事件的成员,数据成员可以是静态数据,类成员总是实例成员,除非用static进行显示声明。一旦实例化PhoncCustomer对象,就可以使用语法Object.FieldName来访问这些字段,如下:

1 PhoneCustomer Customer = new PhoneCustomer();
2 Customer.FirstName="Simon";

2.函数成员:提供了操作类中数据的某些功能,包括方法、属性、构造函数和终结器(finalizer)、运算符以及索引器。

  给方法传递参数可以通过引用或值传递给方法,在通过引用传递(ref,out)时,被调用的方法得到的就是这个变量,所以在方法内部对变量进行的任何改变在方法退出后仍旧有效,而如果变量通过值传递给方法,被调用的方法得到的是变量的一个相同副本,在方法退出后,对变量进行的任何修改会丢失。对于复杂的数据类型,按引用传递效率更高,因为按值传递时必须复制大量的副本。在C#中,除非特殊说明,所有的参数都是通过值来传递。请看下面的例子:

 1 public static int Main() 
2 {
3 int i = 0;
4 int[] ints = {0,1,2,4,8};
5 Console.WriteLine("i = " + i);
6 Console.WriteLine("ints[0] = " + ints[0]);
7 Console.WriteLine("Calling SomeFunction...");
8 someFunction(ints, i);
9 Console.WriteLine("i = " + i);
10 Console.WriteLine("ints[0] = " + ints[0]);
11 return 0;
12 }
13
14 static void someFunction(int[] ints,int i)
15 {
16 ints[0] = 100;
17 i=100;
18 }

输出结果为:

i=0
ints[0]=0
Calling SomeFunction...
i=0
ints[0]=100

注意,i的值保持不变,而在ints中改变的值在原始数组中也改变了!string虽然是引用类型,但是在传递时,对字符串的任何改变不会影响原始字符串。

如果想让方法体内的改变影响到方法外,用ref或out关键字修饰即可。

命名参数:一般参数需要按定义的顺序传送给方法,而命名参数允许按任意顺序传递。如:

 string FullName(string firstName,string lastName)
{
return firstName+""+lastName;
}
下面的调用会返回相同的全名:
FullName("Join","Doe");
FullName(lastName:"Doe",firstName:"Join");

可选参数:必须为可选参数提供默认值,可选参数必须是方法定义的最后一个参数。

方法的重载:C#支持方法重载——方法的多个版本有不同的签名(方法名相同,但参数个数或类型不同)。但是2个方法不能仅在返回类型上有区别,也不能根据参数声明为ref和out来区分。

属性:它是一个方法或一对方法,在客户端代码看来,它是一个字段,如Windows的Height属性:mainForm.Height=400;属性有只读只写属性、自动属性(不能严重有效性,必须为可读写属性)。

3.构造函数:一个与类同名没有返回值的方法。如果不显示声明,系统会自动创建一个默认构造函数。一旦显示声明构造函数,编译器就不会提供默认的构造函数。

  C#的一个特性是可以给类编写一个无参的静态构造函数,这个函数只执行一次,而实例构造函数,只要类创建对象就会执行。编写静态构造函数的一个原因是:类的某些静态字段和属性,需要在第一次使用类之前,从外部源中初始化这些字段和属性。

  构造函数初始化器:实现从构造函数调用其他构造函数,构造函数初始化器在构造函数的函数体之前执行,用this关键字修饰,如下:

 1 class Car
2 {
3 private string description;
4 private int nWheels;
5 public Car(string description,int nWheels)
6 {
7 this.description=description;
8 this.nWheels=nWheels;
9 }
10
11 public Car(string description):this(description,4)
12 {
13
14 }
15 }

在执行Car myCar=new Car("proton persona")时,会先执行带2个参数的构造函数,C#构造函数初始化器可以包含对同一个类的另一个构造函数的调用,也可以包含对直接基类的构造函数的调用,使用base关键字代替this。初始化器不能有多个调用。

  只读字段:类似常量,用readonly关键字修饰,比常量(const修饰)灵活的多,只读字段在运行前其值是未知的,只能在构造函数中给只读字段赋值,类的不同实例只读字段的值可以不同(实例只读字段),也可以是静态的。如果有一个用于编辑文档的MDI程序,因为需要注册,所以要限制可以同时打开的文档数,显然不能在源代码中对最大数目进行硬编码,而是需要一个字段表示,这时就要使用只读字段。注意如果在构造函数中没有给只读字段赋值,它将是特定数据类型的默认值。

三、匿名类型:var关键字与new关键字一起使用时,可以创建匿名类型,匿名类型是一个继承自object且没有名称的类,该类的定义从初始化器中推断,类似隐式类型化的变量,如:

var caption = new {FirstName="James",MiddleName="T",LastName="Kirk"};

就会生成一个包含三个属性的对象。如果在创建一个类似的对象doctor,就可以设置caption=doctor。如果doctor的值来自于caption,可以简化初始化器,如下:

var doctor=new {caption.FirstName,caption.MiddleName,caption.LastName};

这些新对象的类型名未知,编译器为类型‘伪造’了一个名称,只有编译器才能使用它,我们不能使用新对象的任何类型反射。

四、结构:在需要一个小的数据结构时,类提供的功能多于我们需要的功能,由于性能原因,这时最后使用结构。

  结构是值类型,他们存储在栈中或存储为内联(inline),结构不支持继承,编译器总是为结构提供一个无参的默认构造函数,它是不允许替换的,使用结构可以指定字段如何在内存中的布局。

  为结构创建实例时不需要使用new关键字,直接声明即可。注意在结构类型作为参数传递的时候,对结构赋值,结构的所有内容都被复制,而不是复制其引用,这时需要使用ref修饰,以免性能损失。

五、部分类:partial关键字允许把类、结构或接口放在多个文件中。在多个开发人员需要访问同一个类,或者某种类型的代码生成器生成了一个类的某部分,把类放在多个文件中是有意义的。在嵌套类型中,只要partial关键字在class关键字前面,就可以嵌套部分类,在把部分类编译到类型中时,属性、接口、泛型的参数属性和成员都会合并。

六、静态类:如果类只包含静态方法和属性,该类就是静态的,不能创建静态类的实例,静态类中的静态方法调用时只能通过类名调用。

七、Object类:所有.net类的基类,如果定义一个类的时候没有指定基类,编译器就会自动假设这个类派生自Object。所有类都可以调用Object定义的许多公有和受保护的成员方法。如:ToString(),GetHashTable(),Equals()和ReferenceEquals(),Finalize(),GetType(),MemberwiseClone()等方法。

八、扩展方法:在没有源代码时扩展类的功能时使用扩展方法。扩展方法是静态方法,它是类的一部分,但实际上没有放到该类的源代码中。如为Money类增加一个方法AddToAmount(decimal amountToAdd):

1 public static class MoneyExtension
2 {
3 public static void AddToAmount(this Money money,decimal amountToAdd)
4 {
5 money.Amount+=amountToAdd;
6 }
7 }

  对于扩展方法,第一个参数是要扩展的类型,放在this关键字后面,告诉编译器这个方法是Money类的一部分。在扩展方法中可以访问所扩展类的所有公有方法和属性。在主程序中,
AddToAmount方法看起来像是另一个方法,没有显示第一个参数,也不能对其做任何处理,调用时与其他方法相同:cash.AddToAmount(10m);,虽然扩展方法是静态的,但在调用时也要用标准的实例方法语法。如果类中存在与扩展方法同名的方法,此时调用时不会调用扩展方法。

原文地址:https://www.cnblogs.com/PaulMa/p/2248144.html