C# Chapter 9-10
这两章内容比较多也很重要,但写的时候似乎精力有些不如前面了,后面可能会回来再补充,因为我读书也是第一次学,应该存在不少不足之处,后续也许会慢慢完善的。
9 Using Classes and Objects
9.1 Understanding Class Concepts
When you write programs in C#, you create two types of classes:
- Classes that are application programs with a
Main()method. These classes can contain other methods that theMain()method calls, but these classes are not used as blueprints for objects. - Classes from which you instantiate objects. To instantiate an object is to create a tangible example. An object is an instantiation of a class or an instance of a class. A class that is
created to be a model for object instantiations can contain a Main() method, but it is not required.
A data component of a class that differs for each object is stored in an instance variable. An instance variable also is called a field(字段) to help distinguish it from other variables you might use.
The data field values of an object are also called its attributes and the set of all an object’s attributes is known as the object’s state.
这一节介绍的好像比较简略,详见第一章吧
9.2 Creating a Class from Which Objects Can Be Instantiated
When you create a class, you must assign a name to it and determine what data and methods will be part of the class.
字段的修饰符有new, public, protected, internal, private, static, readonly, volatile,默认是private和nonstatic
Using private fields within classes is an example of information hiding, a feature found in all object-oriented languages.The private data of a class should be changed or manipulated only by its own methods, not by methods that belong to other classes.
In contrast to a class’s private data fields, most instance methods are not usually private; they are public. The resulting private data/public method arrangement provides a means to control outside access to your data—only a class’s nonprivate methods can be used to access a class’s private data.
1 | class Employee |
A class can contain fields that are objects of other classes. For example, you might create a class named Date that contains a month, day, and year and add two Date fields to an Employee class to hold the Employee’s birth date and hire date. You might also create a BusinessOffice class and declare Employee objects within it. Using an object as a field within another object is known as composition. The relationship created is also called a has-a relationship because one object “has an” instance of another.
9.3 Creating Objects
Every object name is a reference—that is, it holds a computer memory location where the fields for the object reside. The memory location holds the values for the object.
- When you declare an integer as
int myInteger;, you notify the compiler that an integer namedmyIntegerwill exist, and computer memory automatically is reserved for it at the same time—the exact amount of computer memory depends on the declared data type. - When you declare the
myAssistantinstance of theEmployeeclass, you are notifying the compiler that you will use the identifiermyAssistant. However, you are not yet setting aside computer memory in which the Employee named myAssistant can be stored—that is done only for the built-in, predefined types. To allocate the needed memory for the object, you must use the new operator. You used thenewoperator earlier in this course when you learned to set aside memory for an array.
Because the identifiers for objects are references to their memory addresses, you can call any class a reference type—in other words, a type that refers to a specific memory location. A reference type is a type that holds an address, as opposed to the predefined types such as int, double, and char, each of which is a value type and holds a value as opposed to an address.
人们普遍认为value types不是对象,而reference types是
数据类型也是类
1 | int x = new int(); // 但会被初始化为0 |
int()是constructor(构造函数)
A constructor is a method that instantiates (creates an instance of) an object. 它的名字和类一样
After an object has been instantiated, its nonstatic public members can be accessed using the object’s identifier, a dot, and the nonstatic public member’s identifier.
1 | using static System.Console; |
9.3.1 Passing Objects to Methods
A class represents a data type, and you can pass objects to methods just as you can pass variables that are simple data types.
1 | using static System.Console; |
When you pass an object to a method, you pass a reference. Therefore, any change made to an object parameter in a method also affects the object used as an argument in the calling method.
Because the method accepts an Employee parameter, it must be no more accessible than the Employee class, which is also internal. This restriction preserves the security provided for the Employee class’s nonpublic members.
9.4 Creating Properties
Frequently, methods you call with an object are used to alter the states of its fields. In the Employee class, you might need to change the value for an ID number.
1 | public void SetIdNumber(int number) |
创建properties就是做类似的工作的
A property is a member of a class that provides access to a field of a class; properties define how fields will be set and retrieved(检索). C# programmers refer to properties as smart fields because they provide a combination of the best features of private fields and public methods:
- Like public methods, properties protect private data from outside access.
- Like fields, property names are used in the same way simple variable names are used. When you create properties, the syntax in your client programs becomes more natural and easier to understand.
Properties can have accessor methods that specify the statements that execute when a class’s fields are accessed. Specifically, a property can contain a set accessor for setting an object’s fields and a get accessor for retrieving the stored value.
1 | class Employee |
这里idNumber称为IdNumber的backing field
如果一个property有set accessor,称它可以被written to,如果有get accessor,称它可以被read from,如果它只有get accessor,那么称它为read-only property
accessors跟methods不完全一样
- A
setaccessor acts like a method that accepts a parameter and assigns it to a variable, but you do not include the parameter in a parameter list. Instead, the parameter is accessed using an automatically supplied parameter namedvalue. - A
getaccessor returns the value of the field associated with the property, but you do not code a return type; the return type of agetaccessor is implicitly the type of the property in which it is contained.
事实上,使用set和get很直接
1 | Employee myChef = new Employee(); |
让类的字段保持private并使用accessors去操作它们,让数据保持隐藏的状态,这在面向对象的编程是很重要的特点。此外,accessor可以让你控制类中的数据值,例如,如果你不希望idNumber超过500,可以
1 | set |
不过,property不能作为ref或out参数传入方法
1 | int.TryParse(ReadLine(), out myChef.IdNUmber); // 不编译 |
9.4.1 Using Auto-Implemented Properties
一个简单的property写法
1 | public int IdNumber {get; set;} |
A property written in this format is an auto-implemented property. The property’s implementation (its set of working statements) is created for you automatically with the assumptions that the set accessor should simply assign a value to the appropriate field and the get accessor should simply return the field.
只有同时使用get和set才能用,而且不用声明对应的backing field,编译器会自动生成一个
9.5 More About public and private Access Modifiers
When you define a named constant within a class, it is always static, even though you cannot use the keyword static as a modifier. In other words, the field belongs to the entire class, not to any specific instance of the class. Static and nonstatic fields differ as follows:
- When you create a nonstatic field in a class and create 25 objects, then 25 fields are stored.
- When you create a
staticfield, only one copy is stored for the entire class, no matter how many objects you instantiate.
1 | using static System.Console; |
注意Area只能读,而在Width或Length改变后,都会调用CalcArea()更新area
MOTTO是常量,不能被改变,而且是静态的,使用时是Carpet.MOTTO
9.6 Understanding the this Reference
假如我们有一个类叫Book,每一个Book对象不需要存每个property和method的拷贝值,不同的对象调用函数的结果未必一样,因为它们的数据成员可能不一样,事实上每一个实例方法和property accessor都会被传入一个reference,叫this reference, which is the address of the invoking object.
When you call the method myBook.AdvertisingMessage(), you automatically pass the this reference to the method so that it knows which instance of Book to use. Only nonstatic methods receive a this reference.
当然也可以不写this,例如
1 | using static System.Console; |
有时必须写this,避免混淆
1 | class Book |
9.7 Understanding Constructors
A constructor is a method that instantiates an object. If you do not write a constructor for a class, then each class you create is automatically supplied with a public constructor
with no parameters.
自动创建的constructor会给对象的data fields赋初值
- 数字设为0
- 字符设为’\0’
- 布尔值设为false
- reference设为null
constructor可以重载(overload)
1 | class Employee |
为了避免代码重复,可以使用constructor initializer, a clause that indicates another instance of a class constructor should be executed before any statements in the current constructor body.
1 | class Employee |
这里只有第三个Employee在干活,比如第一个Employee实际上是(根据this里的两个参数)先调用第三个Employee,传入参数empId和0,然后再执行第一个Employee里的内容
readonly修饰符跟const很相似,它用来赋常量值,但是const必须定义时赋值,readonly可以定义时赋值或者在constructor赋值
9.8 Using Object Initializers
An object initializer allows you to assign values to any accessible members or properties of a class at the time of instantiation without calling a constructor with parameters.
1 | Employee aWorker = new Employee{IdNumber = 101}; |
先执行构造函数,再赋值IdNumber
9.9 Overloading Operators
运算符可以重载
1 | class Book |
operator+()是一个method,实现了Books类型的“加法”
1 | using static System.Console; |
可以看到直接用+就行,重载运算符时,至少要有一个参数的类型是这个类
9.10 Declaring an Array of Objects
对象也可以有数组,这时数组里的元素是对象的地址
1 | Employee[] empArray = {new Employee(123), new Employee(456)}; |
同样地我们可以使用Sort()等方法,但问题是要比较只能比较对象的某一个field
An interface is a data type, like a class, except it contains one or more empty methods (and perhaps other members) that can be used by any class.
Each method is an abstract method, which is a method with no body. A class that uses an interface must provide definitions to override the interface’s abstract method definitions. Unlike a class, an interface cannot contain instance fields, and you cannot create objects from it.
A class that implements an interface must override the interface’s methods. When one method overrides another, it takes precedence over the method and is used instead of the original version. When a method overrides another, it has the same signature as the method it overrides. Note that overriding is different from overloading; overloaded methods have different signatures.
接口里的方法是空的,使用接口的类都必须包含这个接口里的方法,每个类可以使用相同的函数名,但函数体未必一样,这就实现了多态性
例如
1 | interface IComparable |
类要使用这个接口,格式见下面的代码,
When you create a class that contains an IComparable.CompareTo() method, the method is an instance method and receives a this reference to the object used to call it. A second object is passed to the method
1 | class Employee : IComparable // 格式 |
这个方法是一个实例方法,它属于一个Employee对象,注意并不是传进的参数,传进的参数是来比较的,它属于的对象就是this指向的对象
1 | int answer = worker1.CompareTo(worker2); |
9.11 Understanding Destructors
A destructor contains the actions you require when an instance of a class is destroyed. Most often, an instance of a class is destroyed when it goes out of scope. As with constructors, if you do not explicitly create a destructor for a class, C# automatically provides one.
析构函数不能有参数,所以一个类只能最多有一个析构函数
1 | class Employee |
析构函数会自动调用,事实上一个对象如果不能再被任何代码使用,也就是"goes out of scope",它就具备了被析构的资格,实际的析构过程可能发生在对象具有被析构的资格后的任何时刻
9.12 GUI
10 Introduction to Inheritance
10.1 Understanding Inheritance
Inheritance is the principle that you can apply your knowledge of a general category to more specific objects.
1 | class Employee |
A class that is used as a basis for inheritance, such as Employee, is called a base class. When you create a class that inherits from a base class (such as CommissionEmployee), it is a derived class or extended class.
派生类还能再派生,一个派生出来的子类的一系列父类称作这个子类的祖先,继承有传递性,子类继承它所有祖先的全部成员(特征+方法)
10.2 Extending Class
1 | class CommissionEmployee : Employee |
Each CommissionEmployee object automatically contains all the fields, properties, and methods of the base class; you then can add new components to the new, derived class.
10.3 Using protected Access Specifier
对于佣金员工,我们想把他的工资设置为0,只能通过Salary这个property,但是Employee类的property里salary最低也是2000,而且尽管子类也有salary这个field,但实际上它不能使用父类的private field,这怎么办呢
The solution is to create the salary field in the parent class with protected access, which provides you with an intermediate level of security between public and private access.
A protected data field or method can be used within its own class or in any classes extended from that class, but it cannot be used by “outside” classes.
1 | class Employee |
10.4 Overriding Base Class Members
虽然派生类拥有基类的所有properties和methods,但可能对于特定的对象实现方法不一样,使用相同的方法或属性名,但实现不同,这被称作多态(polymorphism)
1 | class Student |
Credits is declared to be virtual. A virtual method (or property) is one that can be overridden by a method with the same signature in a child class. When a method overrides another, it takes precedence over the method.
The Credits property is declared with the override modifier because it has the same header (that is, the same signature—the same name and parameter list) as a property in its parent class. The Credits property is said to override and hide its counterpart in the parent class. (You could do the same thing with a method.)
10.4.1 Accessing Base Class Methods and Properties from a Derived Class
1 | class Employee |
子类可以使用base.GetGreeting()来获得基类的GetGreeting()版本,使用new关键词是因为基类并没有加上virtual,不能使用override,派生类的方法与基类方法同名,就只能用new
10.5 Using Implicit Reference Conversions
Every derived object “is a” specific instance of both the derived class and the base class. In other words, myCar “is a” Car as well as a Vehicle, and myDog “is a” Dog as well as a Mammal.
You can assign a derived class object to an object of any of its superclass types. When you do, C# makes an implicit conversion from derived class to base class.
When a derived class object is assigned to its ancestor’s data type, the conversion can more specifically be called an implicit reference conversion. This term is more accurate because it emphasizes the difference between numerical conversions and reference objects.
When you assign a derived class object to a base class type, the object is treated as though it had only the characteristics defined in the base class and not those added in the child class definition.
When a CommissionEmployee class inherits from Employee, an object of either type can be passed to a method that accepts an Employee parameter.
1 | using static System.Console; |
10.6 Using the Object Class
Every class you create in C# derives from a single class named System.Object. In other words, the Object class type in the System namespace is the ultimate base class, or root class, for all other types.
实际上所有类都从这个类派生而来
1 | class Employee |
The keyword object is an alias for the System.Object class.
Because every class descends from Object, every object “is an” Object. As proof, you can write a method that accepts an argument of type Object, and it will accept arguments of any type.
Object类包含一个构造函数、一个析构函数和四个public且非静态的方法
10.6.1 GetType()
返回对象的类型
If you have created an Employee object named someWorker, then the following statement displays Employee:
WriteLine(someWorker.GetType());
If an object’s class is defined in a namespace, then GetType() returns a string composed of the namespace, a dot, and the class name.
10.6.2 ToString()
Object类自带的方法并不是很有用,例如ToString()只会返回这个对象的类的名字(字符串),这样就跟GetType()一样了,所以我们可以根据特别的类来相应override
1 | public override string ToString() |
10.6.3 Equals()
The Equals() method compares objects for reference equality. Reference equality occurs when two reference type objects refer to the same object. The Equals() method returns true if two Objects have the same memory address—that is, if one object is a reference to the other and both are literally the same object.
如果要override这个函数,要满足
- Its header should be as follows (you can use any identifier for the Object parameter, 即o无所谓):
public override bool Equals(Object o) - It should return
falseif the argument is null. - It should return
trueif an object is compared to itself. - It should return
trueonly if both of the following are true:
oneObject.Equals(anotherObject)
anotherObject.Equals(oneObject) - If
neObject.Equals(anotherObject)returns true andoneObject.Equals(aThirdObject)returnstrue, thenanotherObject.Equals(aThirdObject)should also betrue.
10.6.4 GetHashCode()
如果重写了Equals(),也要重写GetHashCode(),因为这个方法如果不重写,就是基于内存地址生成数字(哈希码),两个不同的对象可能内容一样,但地址不一样会导致哈希码也不一样
重写GetHashCode()时要根据业务内容来返回哈希码,比如员工的不同IdNumber对应不同的哈希码
10.7 Working with Base Class Constructors
当实例化一个派生类的对象时,基类的constructor会先执行,然后派生类的constructor执行,因此,实例化任何一个对象时,Object类的constructor都会执行
如果我们创建了一个基类和一个派生类,那么创建派生类对象时,有三个constructor依次执行,Object类、基类、派生类,可以试一下
1 | using static System.Console; |
如果基类只有需要参数的构造函数,那么必须保证子类能够提供参数,因为子类也要调用基类的构造函数
例如我们建立了一个Employee类,它有一个需要两个参数的构造函数,那么子类CommissionEmployee的构造函数可能这么写
1 | public CommissionEmployee() : base(1234, "XXXX") |
10.8 Creating and Using Abstract Classes
A class that is used to instantiate objects is a concrete class. A class that you create only to extend from, but not to instantiate from, is an abstract class. An abstract class is one from which you cannot create concrete objects but from which you can inherit.
Abstract classes are like regular classes in that they can contain data fields and methods. The difference is that you cannot create instances of abstract classes by using the new operator. Rather, you create abstract classes simply to provide a base class from which other objects can be derived.
抽象类可以包含抽象方法,也可以包含不抽象的方法,也可以不包含方法,如果一个类包含抽象方法,任何它的派生类都需要
- 通过给出具体的body来override这个抽象方法
- 可以声明这个方法是abstract的,然后它的子类必须实现这个方法,这时这个类也必须是抽象类,因为抽象方法不能在抽象类或接口外
1 | abstract class Animal |
1 | using static System.Console; |
10.9 Creating and Using Interfaces
When you create a Patent class for each product for which your company holds a patent(专利), you might want to include product information as well as information about the employee who was responsible for the invention. In this situation, it would be convenient to inherit fields and methods from both the Product and Employee classes. The ability to inherit from more than one class is called multiple inheritance.
C#中无法进行多重继承,但可以通过接口来实现类似的效果
An interface is a collection of methods (and perhaps other members) that can be used by any class as long as the class provides a definition to override the interface’s abstract definitions. Within an abstract class, some methods can be abstract, while others need not be. Within an interface, all methods are abstract.
Abstract classes and interfaces are similar in that you cannot instantiate concrete objects from either one. Abstract classes differ from interfaces in that abstract classes can contain nonabstract methods, but all methods within an interface must be abstract. A class can inherit from only one base class (whether abstract or not), but it can implement any number of interfaces.
例如
1 | class Child : Parent, IWorkable, Iplayable |
1 | using static System.Console; |
接口是一种类型,如果对象所属的类实现了接口,编译器就认为它属于这个接口类型,可以理解为它可以实现接口中的那些方法,这似乎在描述它的一系列能力,所以上面的程序还可以这样改
1 | using static System.Console; |
Beginning programmers sometimes find it difficult to decide when to create an abstract base class and when to create an interface. You can follow these guidelines:
- Typically, you create an abstract class when you want to provide some data or methods that derived classes can inherit, but you want the subclasses to override some specific methods that
you declare to be abstract. - You create an interface when you want derived classes to override every method.
In other words, you inherit from an abstract base class when the class you want to create “is a” subtype, and you use an interface when the class you want to create will act like the interface.

