联系我们
简单又实用的WordPress网站制作教学
当前位置:网站首页 > 程序开发学习 > 正文

C# 继承

作者:小教学发布时间:2023-09-28分类:程序开发学习浏览:78


导读:C#继承继承的类型实现继承虚方法隐藏方法调用函数的基类版本抽象类和抽象函数密封类和密封方法派生类的构造函数修饰符访问修饰符其他修饰符接口继承的类型实现继承...

C# 继承

  • 继承的类型
  • 实现继承
  • 虚方法
  • 隐藏方法
  • 调用函数的基类版本
  • 抽象类和抽象函数
  • 密封类和密封方法
  • 派生类的构造函数
  • 修饰符
    • 访问修饰符
    • 其他修饰符
  • 接口

继承的类型

  • 实现继承
    表示一个类型派生于一个基类型,拥有该基类型的所有成员字段和函数。在实现继承中,派生类型的每个函数采用基类型的实现代码,除非在派生类型的定义中指定重写该函数的实现代码。
  • 接口继承
    表示一个类型只继承了函数的签名,没有继承任何实现代码。在需要指定该类型具
    有某些可用的特性时,最好使用这种类型的继承。
  • 多重继承
    一些语言如 C++支持所谓的"多重继承",即一个类派生于多个类。
  • 结构和类
    结构(值类型)和类(引用类型)。使用结构的一个限制是结构不支持继承,但每个结构都自动派生于 System.ValueType。实际上还应更仔细一些:不能建立结构的类型层次,但结构可以实现接口。换言之,结构并不支持实现继承,但支持接口继承。事实上,定义结构和类可以总结为:
    • 结构总是派生于 System.ValueType,它们还可以派生于任意多个接口。
    • 类总是派生于用户选择的另一个类,它们还可以派生于任意多个接口。

实现继承

声明一个类派生于另一个类,可以使用下面的语法:

class MyClass : MyBaseClass
{
	// 函数和数据成员
}

声明一个类继承其他类和接口

class MyClass : MyBaseClass, IMyInterface1, IMyInterface2
{
	// 函数和数据成员
}

声明一个结构继承其他接口

struct MyStruct : IMyInterface1, IMyInterface2
{
	// ...
}

虚方法

把一个基类函数声明为 virtual,该函数就可以在派生类中重写了:

class MyBaseClass
{
	public virtual string VirtualMethod()
	{
		return "base method:VirtualMethod";
	}
}

把一个属性声明为virtual,对于虚属性或重写属性,语法与非虚属性是相同的,但要在定义中
加上关键字 virtual,其语法如下所示

public virtual string ForeName
{
	private string foreName;
	get { return foreName;}
	set { foreName = value;}
}

C#中虚函数的概念与标准 OOP 概念相同:可以在派生类中重写虚函数。在调用方法时,会调用对象类型的合适方法。在 C#中,函数在默认情况下不是虚拟的,但(除了构造函数以外)可以显式地声明为 virtual。

class MyClass : MyBaseClass
{
	public override string VirtualMethod()
	{
		return "override method:VirtualMethod";
	}
}

隐藏方法

如果签名相同的方法在基类和派生类中都进行了声明,但该方法没有声明为 virtual 和 override,派生类方法就会隐藏基类方法。在大多数情况下,是要重写方法,而不是隐藏方法,因为隐藏方法会存在为给定类的实例调用错误方法的危险。
假定有人编写了类 HisBaseClass:

class HisBaseClass
{
}

某一时刻编写了一个派生类,给 HisBaseClass 添加某个功能,特别是要添加一个目前基类中没有的方法 MyGroovyMethod():

class MyDerivedClass : HisBaseClass
{
	public int MyGroovyMethod()
	{
		return 0;
	}
}

一年后,基类的编写者决定扩展基类的功能。为了保持一致,他也添加了一个名为MyGroovyMethod()的方法,该方法的名称和签名与前面添加的方法相同,但并不完成相同的工作。在使用基类的新方法编译代码时,程序在应该调用哪个方法上就会有潜在的冲突。这在 C#中完全合法,但因为我们的 MyGroovyMethod()与基类的 MyGroovyMethod()不相关,运行这段代码的结果就可能不是我们希望的结果。C#已经为此设计了一种方式,可以很好地处理这种情况。
首先,系统会发出警告。在 C#中,应使用 new 关键字声明我们要隐藏一个方法,如下所示:

class MyDerivedClass: HisBaseClass
{
	public new int MyGroovyMethod()
	{
		return 0;
	}
}

调用函数的基类版本

C#有一种特殊的语法用于从派生类中调用方法的基类版本:base.< MethodName >()。

class CustomerAccount
{
	public virtual decimal CalculatePrice()
	{
		return 0.0M;
	}
}

class GoldAccount : CustomerAccount
{
	public override decimal CalculatePrice()
	{
		return base.CalculatePrice() * 0.8M;
	}
}

抽象类和抽象函数

C#允许把类和函数声明为 abstract,抽象类不能实例化,而抽象函数没有执行代码,必须在非抽
象的派生类中重写。显然,抽象函数也是虚拟的(但也不需要提供 virtual 关键字,实际上,如果提供了该关键字,就会产生一个语法错误)。如果类包含抽象函数,该类将也是抽象的,也必须声明为抽象的:

abstract class Building	// 抽象类
{
	private bool damaged = false; // 成员字段初始值
	public abstract decimal CalculateHeatingCost(); // 抽象方法
}

密封类和密封方法

C#允许把类和方法声明为 sealed。对于类来说,这表示不能继承该类;对于方法来说,这表示不能重写该方法。sealed 与java中的final相同。

sealed class FinalClass
{
	//....
}
FinalClass 类不能被其他类继承

class MyClass
{
	public sealed void FinalMethod()
	{
	}
}
FinalMethod不能再MyClass的派生类中重写。

派生类的构造函数

  1. 在层次结构中添加无参数的构造函数
public abstract class GenericCustomer
{
	private string name;
	public GenericCustomer()
	:base() // 使用base表示这是基类构造函数
	{
		name = "< no name >";
	}
	
}
  1. 在层次结构中添加带参数的构造函数
abstract class GenericCutomer
{
	private string name;
	public GenericCutomer(string name)
	{
		this.name = name;
	}
}
class Nevermore60Customer : GenericCutomer
{
	public Nevermore60Customer(string name, string referrerName)
	:base(name)
	{
		this.referrerName = referrerName;
	}
	private string referrerName;
	private uint highCostMinutesUesd;
}

修饰符

访问修饰符

在这里插入图片描述

其他修饰符

在这里插入图片描述

接口

接口有interface声明

public interface IDisposable
{
	void Dispose();
}

类派生接口

class SomeClass:IDisposable
{
	public void Dispose()
	{
		// 实现接口方法
	}
}

接口的定义

namespace Wrox.ProCSharp
{
	public interface IBankAccount
	{
		void PlayIn(decimal amount);
		bool Withdraw(decimal amount);
		decimal Balance
		{
			get;
		}
	}

}

接口的继承

namespace Wrox.ProCSharp.VenusBank
{
	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("error.");
			return false;
		}
		public decimal Balance
		{
			get 
			{
				return balance;	
			}
		}
		public override string ToString()
		{
			return String.Format("Vens Bank Saver: Balance = {0,6:C}", balance);
		}
	}

}

不同类实现相同的接口

namespace Wrox.ProCSharp.JupiterBank 
{
	public class GoldAccount:IBankAccount
	{
		// ...
	}
}

测试代码

using System;
using Wrox.ProCSharp;
using Wrox.ProCSharp.VenusBank;
using Wrox.ProCSharp.JupiterBank;

namespace Wrox.ProCSharp
{
	class MainEntryPoint
	{
		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());
		}
	}
}

接口数组

IBankAccount[] accounts = new IBankAccount[2];

accounts[0] = new SaverAccount();
accounts[1] = new GoldAccount();

派生接口
接口可以彼此继承,其方式与类的继承相同。

namespace Wrox.ProCSharp
{
	public interface ITransferBankAccount: IBankAccount
	{
		bool TransferTo(IBankAccount desination, 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 failed.");
		return false;
	}
	public decimal Balance
	{
		get { return balance;}
	}
	public bool TransferTo(IBankAccount destination, decimal amount)
	{
		bool result;
		if ((result = Withdraw(amount)) == true)
		{
			destination.PayIn(amount);
			return result;
		}
	}
	
	public override string ToString()
	{
		return String.Format("Jupiter Bank Current Account:Balance = {0, 6:C}", balance);
	}
}

// 验证代码

static void Main()
{
	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());
}





程序开发学习排行
最近发表
网站分类
标签列表