1. 继承的类型
1.1 实现继承和接口继承
(1)实现继承:表示一个类型派生于一个基类型,它拥有该基类型的所有成员字段和函数。在实现继承中,派生类型采用基类型的每个函数的实现代码,除非在派生类型的定义中指定
重写某个函数的实现代码。在需要给现有的类型添加功能,或许多相关的类型共享一组重要的公共功能时,这种类型的继承非常有用。
(2)接口继承:表示一个类型只继承了函数的签名,没有继承任何实现代码。在需要指定该类型具有某些可用的特性时,最好使用这种类型的继承。
1.2 多重继承:
(1)C#不支持多重实现继承,但允许类型派生自多个接口——多重接口继承。
1.3 结构和类
(1)使用结构的一个限制是结构不支持继承,但每个结构都自动派生自system.ValueType。
(2)结构并不支持实现继承,但支持接口继承。
(3)结构总是派生自system.ValueType,它们还可以派生自任意多个接口;类总是派生自用户选择的另一个类,它们还可以派生自任意多个接口。
2. 实现继承
(1)如果要声明派生自另一个类的一个类,就可以使用下面的语法:
class MyDerivedClass: MyBaseclass
{
// functions and data members here
}
(2)对于结构,语法如下:
public struct MyDerivedStruct: IInterface1、 IInterface2
{
// etc.
}
2.1 虚方法
(1)把一个基类函数声明为virtual,就可以在任何派生类中重写该函数:
class MyBaseClass
{
public virtual string VirtualMethod()
{
return "This method is virtual and defined in MyBaseClass";
}
}
(2)也可以把属性声明为virtual。对于虚属性或重写属性,语法与非虚属性相同,但要在定义中添
加关键字virtual,其语法如下所示:
public virtual string ForeName
{
get { return foreName;}
set { foreName =value;}
}
private string foreName;
(3)C#要求在派生类的函数重写另一个函数时,要使用override关键字显式声明:
class MyDerivedClass: MyBaseClass
{
public override string VirtualMethod()
{
return " This method is an override defined in MyDerivedClass.";
}
}
成员字段和静态函数都不能声明为virtual。
2.2 隐藏方法
(1)如果签名相同的方法在基类和派生类中都进行了声明,但该方法没有分别声明为virtual和override,派生类方法就会隐藏基类方法。
(2)在C#中,要隐藏一个方法应使用new关键字声明,如下所示:
class MyDer△ vedC1ass: HisBaseClass,
{
public new int MyGroovyMethod()
{
// some groovy implementation
return 0;
}
}
(3)如果愿意,就可以给新方法重命名。最好这么做,因为这会避免许多冲突。
2.3 调用函数的基类版本
(1)C#有一种特殊的语法用于从派生类中调用方法的基类版本:base.<MethodName>()。
(2)例如,假定派生类中的一个方法要返回基类的方法90%的返回值,就可以使用下面的语法:
class CustomerAccount
{
public virtual decimal CalculatePrice()
{
// implementation
return 0.0M;
}
}
class GoldAccount: CustomerAccount
{
pub1ic override decimal CalculatePrice()
{
return base.CalculatePrice() * 0.9M;
}
}
2.4 抽象类和抽象函数
(1)C#允许把类和函数声明为abstract。抽象类不能实例化,而抽象函数不能直接实现,必须在非抽象的派生类中重写。
(2)如果类包含抽象函数,则该类也是抽象的,也必须声明为抽象的:
abstract class Building
{
public abstract decimal CalculateHeatingCost(); // abstract method
}
2.5 密封类和密封方法
(1)C#允许把类和方法声明为seaIed。对于类,表示不能继承该类;对于方法,表示不能重写该方法。
sealed class FinalClass
{
// etc
}
class DerivedClass:FinalClass // wrong. Will give compilation error
{
// etc
}
(2)在把类或方法标记为sealed时,最可能的情形是:如果要对库、类或自己编写的其他类作用域之外的类或方法进行操作,则重写某些功能会导致代码混乱。也可以因商业原因把类或方法标记为sealed,以防第三方以违反授权协议的方式扩展该类。
(3)要在方法或属性上使用sealed关键字,必须先从基类上把它声明为要重写的方法或属性。如果基类上不希望有重写的方法或属性,就不要把它声明为叫virtual。
2.6 派生类的构造函数
(1)代码:
namespace study4_2_6override
{
abstract class GenericCustomer
{
private string name;
public GenericCustomer(string name)
{
this.name = name;
}
}
class nevermore60Customer:GenericCustomer
{
private uint highCostMinutesUsed;
private string referrername;
public nevermore60Customer(string name,string referrerName):base(name)
{
this.referrername = referrerName;
}
public nevermore60Customer(string name)
: this(name, "<NOne>")
{
}
}
class Program
{
static void Main(string[] args)
{
GenericCustomer customer = new nevermore60Customer("<no name>");
}
}
}
(2)在本例中Nevermore60Customer这个默认Nevermore60Customer构造函数首先要做的是为其直接基类GenericCustomer运行默认构造函数,然后GenericCustomer构造函数为其直接基类Systm.Object运行默认构造函数,System.Object没有任何基类,所以它的构造函数就执行,并把控制权返回给GenericCustomer构造函数。现在执行GenericCustomer构造函数,把Name初始化为null,再把控制权返回给Nevermore60Customer构造函数,接着执行这个构造函数,把highCostMinutesUsed初始化为0,并退出。此时Nevermore60Customer实例就已经成功地构造和初始化了。
(3)所有操作的最终结果是,构造函数的调用顺序是先调用System.Object,再按照层次结构由上向下进行,直到到达编译器要实例化的类为止
(4)注意构造函数的执行顺序。总是最先调用的正是基类的构造函数。也就是说,派生类的构造函数可以在执行过程中调用它可以访问的任何基类方法、属性和任何其他成员,因为基类已经构造出来了,其字段也初始化了。
2.6.1 在层次结构中添加无参数的构造函数
(1)这次使用的关键字是base,而不是this,表示这是基类的构造函数,而不是要调用的当前类的构造函数。在base关键字后面的圆括号中没有参数,这非常重要,因为没有给基类构造函数传送任何参数,所以编译器必须调用无参数的构造函数。
(2)base和this关键字是调用另一个构造函数时允许使用的唯一关键字,其他关键字都会产生编译错误。还要注意只能指定唯一一个其他的构造函数。
2.6.2 在层次结构中添加带参数的构造函数
3. 修饰符
3.1 可见性修饰符
(1)这些修饰符只能应用于成员。并可以用这些修饰符定义嵌套的类型。
3.2 其他修饰符
4. 接口
(1)如果一个类派生自一个接口,声明这个类就会实现某些函数。
(2)声明接口在语法上与声明抽象类完全相同,但不允许提供接口中任何成员的实现方式。一般情况下,接口只能包含方法、属性、索引器和事件的声明。
(3)不能实例化接口,它只能包含其成员的签名。接口既不能有构造函数也不能有字段。接口定义也不允许包含运算符重载。
(4)在接口定义中不允许声明关于成员的修饰符。
4.1 定义和实现接口
(1)代码:
namespace study4_4_1
{
//银行账户接口
public interface IBankAccount
{
void PayIn(decimal amount);
bool Withdraw(decimal amount);
decimal Balance { get; }
}
//银行账户
public class SaverAccount:IBankAccount
{
private decimal balance;
public void PayIn(decimal amount)
{
balance += amount;
}
public bool Withdraw(decimal amount)
{
if (balance >= amount)
{
balance -= amount;
return true;
}
Console.WriteLine("Withdrawal attempt failed.");
return false;
}
public decimal Balance
{
get
{
return balance;
}
}
public override string ToString()
{
return String.Format("Venus Bank Saver: Balance = {0,6:C}", balance);
}
}
public class GoldAccount:IBankAccount
{
private decimal balance;
public void PayIn(decimal amount)
{
balance += amount;
}
public bool Withdraw(decimal amount)
{
if (balance >= amount)
{
balance -= amount;
return true;
}
Console.WriteLine("Withdrawal attempt failed.");
return false;
}
public decimal Balance
{
get
{
return balance;
}
}
public override string ToString()
{
return String.Format("Venus Bank Saver: Balance = {0,6:C}", balance);
}
}
class Program
{
static void Main(string[] args)
{
IBankAccount venusAccount = new SaverAccount();
IBankAccount jupiterAccount = new GoldAccount();
venusAccount.PayIn(200);
venusAccount.Withdraw(100);
Console.WriteLine(venusAccount.ToString());
jupiterAccount.PayIn(500);
jupiterAccount.Withdraw(600);
jupiterAccount.Withdraw(100);
Console.WriteLine(jupiterAccount.ToString());
}
}
}
(2)结果:
4.2 派生的接口
(1)接口可以彼此继承,其方式与类的继承方式相同。
(2)代码:
namespace study4_4_1
{
//银行账户接口
public interface IBankAccount
{
void PayIn(decimal amount);
bool Withdraw(decimal amount);
decimal Balance { get; }
}
public interface ITransferBankAccount:IBankAccount
{
bool TransferTo(IBankAccount destionation, decimal amount);
}
public class CurrentAccount:ITransferBankAccount
{
private decimal balance;
public void PayIn(decimal amount)
{
balance += amount;
}
public bool Withdraw(decimal amount)
{
if (balance >= amount)
{
balance -= amount;
return true;
}
Console.WriteLine("Withdrawal attempt failed.");
return false;
}
public decimal Balance
{
get
{
return balance;
}
}
public bool TransferTo(IBankAccount destination, decimal amount)
{
bool result;
result = Withdraw(amount);
if (result)
{
destination.PayIn(amount);
}
return result;
}
public override string ToString()
{
return String.Format("Jupiter Bank Current Account: Balance = {0,6:C}", balance);
}
}
//银行账户
public class SaverAccount:IBankAccount
{
private decimal balance;
public void PayIn(decimal amount)
{
balance += amount;
}
public bool Withdraw(decimal amount)
{
if (balance >= amount)
{
balance -= amount;
return true;
}
Console.WriteLine("Withdrawal attempt failed.");
return false;
}
public decimal Balance
{
get
{
return balance;
}
}
public override string ToString()
{
return String.Format("Venus Bank Saver: Balance = {0,6:C}", balance);
}
}
class Program
{
static void Main(string[] args)
{
IBankAccount venusAccount = new SaverAccount();
ITransferBankAccount jupiterAccount = new CurrentAccount();
venusAccount.PayIn(200);
jupiterAccount.PayIn(500);
jupiterAccount.TransferTo(venusAccount, 100);
Console.WriteLine(venusAccount.ToString());
Console.WriteLine(jupiterAccount.ToString());
}
}
}
(3)结果: