c#重寫和多態
多態是基于重寫的
- 繼承:向子類中添加父類沒有的成員,子類對父類的橫向擴展
- 重寫:縱向擴展,成員沒有增加,但成員的版本增加了
引言
Rider
JetBrains:Rider、ReSharper、dotPeek
Rider 支持包括 .NET Core 在內的較全面的 .NET 開發,以及 Unity 開發。
.NET Core / ASP.NETWeb Application 開發包括:
- RestFul API
- .NET Core 網站開發
Q:Rider 是否支持 WinForm 開發?
A:不支持 WinForm,但支持基于 XAML 的 WPF 和 Xamarin
Java 常用 Spring + Hibernate + JPA 這一套,它們都能在 .NET Core 找到對應選擇。
ASP.NET Core 框架 + Entity Framework Core
.NET Core 自帶 Razor engine
Edx Timothy 參與開發的課程
校長關于 ASP.NET Core 開發的一些課程,分別講 LINQ、Web 開發基礎、RestFul API、實戰。
未來還將有一門 ASP.NET Core 高級開發和 Authentication & Authorization。

DFS 與 BFS
- DFS:Depth-First-Search 深度優先搜索
- BFS:Breadth-First Search 廣度優先搜索
using System;
using System.Collections.Generic;
using System.Linq;
namespace ConsoleTemp
{
// 注:為了方便理解,很多變量命名都用的全稱
class Program
{
static void Main(string[] args)
{
// 生成 [0,10) 的自然數數組,即 0,1,2,3...9
var values = Enumerable.Range(0, 10).ToArray();
var binarySearchTree = GetTree(values, 0, values.Length - 1);
DFS(binarySearchTree);
Console.WriteLine("============================");
BFS(binarySearchTree);
}
static Node GetTree(int[] values, int lowIndex, int highIndex)
{
if (lowIndex > highIndex) return null;
var middleIndex = lowIndex + (highIndex - lowIndex) / 2;
var node = new Node(values[middleIndex]);
node.Left = GetTree(values, lowIndex, middleIndex - 1);
node.Right = GetTree(values, middleIndex + 1, highIndex);
return node;
}
static void DFS(Node node)
{
if (node == null) return;
DFS(node.Left);
Console.WriteLine(node.Value);
DFS(node.Right);
}
static void BFS(Node root)
{
var q = new Queue<Node>();
q.Enqueue(root);
while (q.Count > 0)
{
var node = q.Dequeue();
Console.WriteLine(node.Value);
if (node.Left != null) q.Enqueue(node.Left);
if (node.Right != null) q.Enqueue(node.Right);
}
}
}
class Node
{
public int Value { get; set; }
public Node Left { get; set; }
public Node Right { get; set; }
public Node(int value)
{
Value = value;
}
}
}

C# 語言標準文檔
C# 5.0 已經成為國際標準 ECMA-334,ECMA-334 的 PDF 比微軟自己的標準文檔還要權威。
C# 6.0 7.0 還在 ECMA 驗證中。
筆者注:校長還是很注重標準文檔,推薦有志于深入 C# 的同學去多翻一翻、讀一讀。
下面開始講解本節的正式內容:
本節內容
- 類的繼承
- 類成員的“橫向擴展”(成員越來越多)
- 類成員的“縱向擴展”(行為改變,版本增高)
- 類成員的隱藏(不常用)
- 重寫與隱藏的發生條件:函數成員,可見,簽名一致
- 多態(polymorphism)
- 基于重寫機制(virtual -> override)
- 函數成員的具體行為(版本)由對象決定
- 回顧:C# 語言的變量和對象都是有類型的,所以會有“代差”
Override 重寫
子類對父類成員的重寫。
因為類成員個數還是那么多,只是更新版本,所以又稱為縱向擴展。
注:重寫時,Car 里面只有一個版本的 Run。
重寫需要父類成員標記為 virtual,子類成員標記 override。
注:被標記為 override 的成員,隱含也是 virtual 的,可以繼續被重寫。
virtual:可被重寫的、名義上的、名存實亡的
class Program
{
static void Main(string[] args)
{
var car = new Car();
car.Run();
// Car is running!
var v = new Vehicle();
v.Run();
// I'm running!
}
}
class Vehicle
{
public virtual void Run()
{
Console.WriteLine("I'm running!");
}
}
class Car : Vehicle
{
public override void Run()
{
Console.WriteLine("Car is running!");
}
}
Hide
如果子類和父類中函數成員簽名相同,但又沒標記 virtual 和 override,稱為 hide 隱藏。

這會導致 Car 類里面有兩個 Run 方法,一個是從 Vehicle 繼承的 base.Run(),一個是自己聲明的 this.Run()。
可以理解為 v 作為 Vehicle 類型,它本來應該順著繼承鏈往下(一直到 Car)找 Run 的具體實現,但由于 Car 沒有 Override,所以它找不下去,只能調用 Vehicle 里面的 Run。
class Program
{
static void Main(string[] args)
{
Vehicle v = new Car();
v.Run();
// I'm running!
}
}
class Vehicle
{
public void Run()
{
Console.WriteLine("I'm running!");
}
}
class Car : Vehicle
{
public void Run()
{
Console.WriteLine("Car is running!");
}
}
注:
- 新手不必過于糾結 Override 和 Hide 的區分、關聯。因為原則上是不推薦用 Hide 的。很多時候甚至會視 Hide 為一種錯誤
- Java 里面是天然重寫,不必加 virtual 和 override,也沒有 Hide 這種情況
- Java 里面的 @Override(annotation)只起到輔助檢查重寫是否有效的功能
Polymorphism 多態
C# 支持用父類類型的變量引用子類類型的實例。
函數成員的具體行為(版本)由對象決定。
回顧:因為 C# 語言的變量和對象都是有類型的,就導致存在變量類型與對象類型不一致的情況,所以會有“代差”。
class Program
{
static void Main(string[] args)
{
Vehicle v = new RaceCar();
v.Run();
// Race car is running!
Car c = new RaceCar();
c.Run();
// Race car is running!
Console.ReadKey();
}
}
class Vehicle
{
public virtual void Run()
{
Console.WriteLine("I'm running!");
}
}
class Car : Vehicle
{
public override void Run()
{
Console.WriteLine("Car is running!");
}
}
class RaceCar : Car
{
public override void Run()
{
Console.WriteLine("Race car is running!");
}
}
C# vs Python
Python 是對象有類型,變量沒有類型的語言,Python 變量的類型永遠跟著對象走。 所以在 Python 中即使重寫了,也沒有多態的效果。

PS:
- JS 和 Python 類似,也是對象有類型,變量沒類型
- TypeScript 是基于 JS 的強類型語言,所以 TS 變量是有類型的,存在多態
重寫三條件
函數成員
只有函數成員才能重寫,最常用的是重寫 Methods 和 Properties。
函數成員的定義:

重寫屬性示例:
class Program
{
static void Main(string[] args)
{
Vehicle v = new Car();
v.Run();
// "Car is running!"
Console.WriteLine(v.Speed);
// 50
}
}
class Vehicle
{
private int _speed;
public virtual int Speed
{
get { return _speed; }
set { _speed = value; }
}
public virtual void Run()
{
Console.WriteLine("I'm running!");
_speed = 100;
}
}
class Car : Vehicle
{
private int _rpm;
public override int Speed
{
get { return _rpm / 100; }
set { _rpm = value * 100; }
}
public override void Run()
{
Console.WriteLine("Car is running!");
_rpm = 5000;
}
}
可見
只有對子類可見的父類成員可以重寫,具體說就是 protected 和 public。例如子類能繼承父類 private 的成員,但無法訪問,即不可見、不可重寫。
訪問級別的更多內容參考 https://www.yuque.com/yuejiangliu/dotnet/timothy-csharp-024-025#c75846f4。
簽名一致
方法簽名:方法名稱 + 類型形參的個數 + 每個形參(從左往右)的類型和種類(值、引用或輸出)。
注:下面要講接口和抽象類,為了與本節內容混淆,必須把本節徹底消化吸收。

浙公網安備 33010602011771號