• C# 继承


    继承的类型

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

    实现继承

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

    class MyClass : MyBaseClass
    {
    	// 函数和数据成员
    }
    
    • 1
    • 2
    • 3
    • 4

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

    class MyClass : MyBaseClass, IMyInterface1, IMyInterface2
    {
    	// 函数和数据成员
    }
    
    • 1
    • 2
    • 3
    • 4

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

    struct MyStruct : IMyInterface1, IMyInterface2
    {
    	// ...
    }
    
    • 1
    • 2
    • 3
    • 4

    虚方法

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

    class MyBaseClass
    {
    	public virtual string VirtualMethod()
    	{
    		return "base method:VirtualMethod";
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

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

    public virtual string ForeName
    {
    	private string foreName;
    	get { return foreName;}
    	set { foreName = value;}
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

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

    class MyClass : MyBaseClass
    {
    	public override string VirtualMethod()
    	{
    		return "override method:VirtualMethod";
    	}
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    隐藏方法

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

    class HisBaseClass
    {
    }
    
    • 1
    • 2
    • 3

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

    class MyDerivedClass : HisBaseClass
    {
    	public int MyGroovyMethod()
    	{
    		return 0;
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

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

    class MyDerivedClass: HisBaseClass
    {
    	public new int MyGroovyMethod()
    	{
    		return 0;
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    调用函数的基类版本

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

    class CustomerAccount
    {
    	public virtual decimal CalculatePrice()
    	{
    		return 0.0M;
    	}
    }
    
    class GoldAccount : CustomerAccount
    {
    	public override decimal CalculatePrice()
    	{
    		return base.CalculatePrice() * 0.8M;
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    抽象类和抽象函数

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

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

    密封类和密封方法

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

    sealed class FinalClass
    {
    	//....
    }
    FinalClass 类不能被其他类继承
    
    class MyClass
    {
    	public sealed void FinalMethod()
    	{
    	}
    }
    FinalMethod不能再MyClass的派生类中重写。
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    派生类的构造函数

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

    修饰符

    访问修饰符

    在这里插入图片描述

    其他修饰符

    在这里插入图片描述

    接口

    接口有interface声明

    public interface IDisposable
    {
    	void Dispose();
    }
    
    • 1
    • 2
    • 3
    • 4

    类派生接口

    class SomeClass:IDisposable
    {
    	public void Dispose()
    	{
    		// 实现接口方法
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    接口的定义

    namespace Wrox.ProCSharp
    {
    	public interface IBankAccount
    	{
    		void PlayIn(decimal amount);
    		bool Withdraw(decimal amount);
    		decimal Balance
    		{
    			get;
    		}
    	}
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    接口的继承

    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);
    		}
    	}
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33

    不同类实现相同的接口

    namespace Wrox.ProCSharp.JupiterBank 
    {
    	public class GoldAccount:IBankAccount
    	{
    		// ...
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    测试代码

    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());
    		}
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    接口数组

    IBankAccount[] accounts = new IBankAccount[2];
    
    accounts[0] = new SaverAccount();
    accounts[1] = new GoldAccount();
    
    • 1
    • 2
    • 3
    • 4

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

    namespace Wrox.ProCSharp
    {
    	public interface ITransferBankAccount: IBankAccount
    	{
    		bool TransferTo(IBankAccount desination, decimal amount);
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    派生接口类

    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());
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
  • 相关阅读:
    JLINK掉固件后重刷
    ESP32C3 开发板 Download Mode 模式
    JSON实例操作
    【Linux】进程地址空间
    【Docker】实现JMeter分布式压测
    axios
    【Axure 答疑】页面滚动到一定位置显示功能按钮
    深入浅出索引
    泛型的使用
    spring cron表达式源码分析
  • 原文地址:https://blog.csdn.net/u013420428/article/details/133345043