完成了 C# 入门之后,就可以进入 C# 基础阶段。
一、命令行参数 命令行会给 C# 程序的 Main
方法以字符串数组的形式传递参数(可选),正如 Main
的方法签名通常为 static void Main(string[] args)
。
默认情况下,命令行使用空格分隔相邻的参数。如果一个参数里面包含空格,则需要用双引号包裹这个参数,如:
命令行上的输入
传递给 Main 的字符串数组
executable.exe a b c
"a"
"b"
"c"
executable.exe one two
"one"
"two"
executable.exe "one two" three
"one two"
"three"
这个例子调用了命令行参数:
1 2 3 4 5 6 Console.WriteLine($"parameter count = {args.Length} " ); for (int i = 0 ; i < args.Length; i++){ Console.WriteLine($"Arg[{i} ] = [{args[i]} ]" ); }
二、C# 的面向对象编程(一):抽象和封装 2.1 定义类 从这个例子入手,在工程中新建一个 .cs 文件,里面写上:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 namespace CustomizedClasses ;public class BankAccount { public string Number { get ; } public string Owner { get ; set ; } public decimal Balance { get ; } public void MakeDeposit (decimal amount, DateTime date, string note ) { } public void MakeWithdrawal (decimal amount, DateTime date, string note ) { } }
namespace
声明了一个命名空间,组织其后面跟随的类。
public class BankAccount
定义创建的类,大括号包裹的类体中定义类的成员。publlic
允许被外部命名空间或程序集访问。
此时 BankAccount
有 5 个成员。前三个是属性,后面两个是方法。属性就是带有访问器 { get; set; }
的数据成员,方法就是类体中可复用的代码块。
因此,类定义就是在命名空间中定义集成各个类成员的类型系统。
2.2 构造函数 在前面的例子中,给 MakeDeposit
方法声明前面添加:
1 2 3 4 5 public BankAccount (string name, decimal initialBalance ) { this .Owner = name; this .Balance = initialBalance; }
这就是一个构造函数,使用类名做标识符。它在实例化时被调用,this
表示类的实例,如果没有局部变量冲突可以省略。此时可以在顶级语句或 Main
方法里面写:
1 2 3 4 5 6 using CustomizedClasses;var account1 = new BankAccount("name1" , 1000 );Console.WriteLine($"Account {account1.Number} was created for {account1.Owner} with {account1.Balance} initial balance." ); var account2 = new BankAccount("name2" , 2000 );Console.WriteLine($"Account {account2.Number} was created for {account2.Owner} with {account2.Balance} initial balance." );
此时已经把刚才创建的类进行了实例化,也就是从账户类这个模板创建了一个账户,并初始化了名字和余额。但是此时账号为空。
2.3 字段 给前面的 BankAccount
类体添加:
1 private static int s_accountNumberSeed = 1234567890 ;
此时定义了字段 accountNumberSeed
。
该字段使用 private static
修饰,private
意味着它只能在类体被访问,static
意味着它由所有实例共享。
s_
前缀是根据 C# 命名约定写上的,s
表示 static
,而 _
表示 private
。
大多数情况下,我们使用 public
修饰属性,使用 private
修饰字段。
添加以下代码至构造函数,就可以实现账号分配:
1 2 Number = s_accountNumberSeed.ToString(); s_accountNumberSeed++;
调试程序,可见输出:
1 2 Account 1234567890 was created for name1 with 1000 initial balance. Account 1234567891 was created for name2 with 2000 initial balance.
2.4 定义多个类并交互 定义多一个类,表示交易:
1 2 3 4 5 6 7 8 9 10 11 12 13 public class Transaction { public decimal Amount { get ; } public DateTime Date { get ; } public string Notes { get ; } public Transaction (decimal amount, DateTime date, string note ) { Amount = amount; Date = date; Notes = note; } }
给 BankAccount
类添加一个交易记录列表:
1 private List<Transaction> _allTransactions = new ();
将 Balance
属性替换为
1 2 3 4 5 6 7 8 9 10 11 12 13 public decimal Balance{ get { decimal balance = 0 ; foreach (var item in _allTransactions) { balance += item.Amount; } return balance; } }
此时由于定义了 get
访问器,所以 Balance
属性不再是自动属性,而是只读属性,它是一个被计算而得的属性。
写入 BankAccount
存款和取款的方法(前面声明过,此时填入即可):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 public void MakeDeposit (decimal amount, DateTime date, string note ) { if (amount <= 0 ) { throw new ArgumentOutOfRangeException(nameof (amount), "Amount of deposit must be positive" ); } var deposit = new Transaction(amount, date, note); _allTransactions.Add(deposit); } public void MakeWithdrawal (decimal amount, DateTime date, string note ) { if (amount <= 0 ) { throw new ArgumentOutOfRangeException(nameof (amount), "Amount of withdrawal must be positive" ); } if (Balance - amount < 0 ) { throw new InvalidOperationException("Not sufficient funds for this withdrawal" ); } var withdrawal = new Transaction(-amount, date, note); _allTransactions.Add(withdrawal); }
因为 Balance
属性变为了只读,所以无法被赋予值,BankAccount
的构造函数此时也应修改为:
1 2 3 4 5 6 7 8 public BankAccount (string name, decimal initialBalance ){ Number = s_accountNumberSeed.ToString(); s_accountNumberSeed++; Owner = name; MakeDeposit(initialBalance, DateTime.Now, "Initial balance" ); }
回到顶级语句或 Main
方法,添加调试代码:
1 2 3 4 account1.MakeWithdrawal(300 , DateTime.Now, "Rent payment" ); Console.WriteLine(account1.Balance); account1.MakeDeposit(100 , DateTime.Now, "Friend paid me back" ); Console.WriteLine(account1.Balance);
输出内容如下:
1 2 3 4 Account 1234567890 was created for name1 with 1000 initial balance. Account 1234567891 was created for name2 with 2000 initial balance. 700 800
回顾刚才的代码,进行一点修饰并添加查看交易记录的功能,可得两个 .cs
文件:
BankAccount.cs
:
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 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 namespace CustomizedClasses ;public class BankAccount { private static int s_accountNumberSeed = 1234567890 ; public string Number { get ; } public string Owner { get ; set ; } public decimal Balance { get { decimal balance = 0 ; foreach (var item in _allTransactions) { balance += item.Amount; } return balance; } } private List<Transaction> _allTransactions = new (); public BankAccount (string name, decimal initialBalance ) { Number = s_accountNumberSeed.ToString(); s_accountNumberSeed++; Owner = name; MakeDeposit(initialBalance, DateTime.Now, "初始余额" ); } public void MakeDeposit (decimal amount, DateTime date, string note ) { if (amount <= 0 ) { throw new ArgumentOutOfRangeException(nameof (amount), "Amount of deposit must be positive" ); } var deposit = new Transaction(amount, date, note); _allTransactions.Add(deposit); } public void MakeWithdrawal (decimal amount, DateTime date, string note ) { if (amount <= 0 ) { throw new ArgumentOutOfRangeException(nameof (amount), "Amount of withdrawal must be positive" ); } if (Balance - amount < 0 ) { throw new InvalidOperationException("Not sufficient funds for this withdrawal" ); } var withdrawal = new Transaction(-amount, date, note); _allTransactions.Add(withdrawal); } public string GetAccountHistory () { var report = new System.Text.StringBuilder(); decimal balance = 0 ; report.AppendLine("日期\t\t交易\t余额\t备注" ); foreach (var item in _allTransactions) { balance += item.Amount; report.AppendLine($"{item.Date.ToShortDateString()} \t{item.Amount} \t{balance} \t{item.Notes} " ); } return report.ToString(); } } public class Transaction { public decimal Amount { get ; } public DateTime Date { get ; } public string Notes { get ; } public Transaction (decimal amount, DateTime date, string note ) { Amount = amount; Date = date; Notes = note; } }
Program.cs
:
1 2 3 4 5 6 7 8 9 10 11 12 13 using CustomizedClasses;var account1 = new BankAccount("Tim" , 1000 ); Console.WriteLine($"用户 {account1.Number} 被 {account1.Owner} 初始化了 {account1.Balance} 的金额." ); var account2 = new BankAccount("Jane" , 2000 ); Console.WriteLine($"用户 {account2.Number} 被 {account2.Owner} 初始化了 {account2.Balance} 的金额." ); account1.MakeWithdrawal(300 , DateTime.Now, "买车票" ); Console.WriteLine($"{account1.Owner} 支出了" + account1.Balance); account1.MakeDeposit(100 , DateTime.Now, "补贴" ); Console.WriteLine($"{account1.Owner} 存入了" + account1.Balance); Console.WriteLine(account1.GetAccountHistory());
三、C# 的面向对象编程(二):继承和多态 3.1 创建类的继承 继续完善刚才的银行系统。根据银行账户存在不同需求,于是请求以下账户类型:
通过这样的语法,我们可以创建对 BankAccount
类的三种继承:
1 2 3 4 5 6 7 8 9 10 11 public class InterestEarningAccount : BankAccount { } public class LineOfCreditAccount : BankAccount { } public class GiftCardAccount : BankAccount { }
最好从不同的源文件中创建每个类,因此我们可以新建三个和这三种账户类名同名的源文件,然后把类声明放进去。
3.2 构造函数的继承 编译器不会生成被继承的构造函数,因此需要派生类显式调用此构造函数,以下代码显示了 InterestEarningAccount
类的构造函数:
1 2 3 public InterestEarningAccount (string name, decimal initialBalance ) : base (name, initialBalance ){ }
3.3 方法多态 在 BankAccount
类添加 PerformMonthEndTransactions
方法:
1 public virtual void PerformMonthEndTransactions () { }
基类中的方法使用 virtual
修饰符允许派生类覆盖重写该方法,以实现多态。
从月末交易方法的角度,再描述前面提出的三种账户派生类的要求:
随后,依次给三个账户派生类也添加 PerformMonthEndTransactions
方法。
从 InterestEarningAccount
类开始:
1 2 3 4 5 6 7 8 public override void PerformMonthEndTransactions (){ if (Balance > 500 m) { decimal interest = Balance * 0.02 m; MakeDeposit(interest, DateTime.Now, "按月放息" ); } }
然后是 LineOfCreditAccount
类:
1 2 3 4 5 6 7 8 public override void PerformMonthEndTransactions (){ if (Balance < 0 ) { decimal interest = -Balance * 0.07 m; MakeWithdrawal(interest, DateTime.Now, "按月收息" ); } }
而在 GiftCardAccount
类需要先修改构造函数,并加一个只读字段 _monthlyDeposit
:
1 2 3 4 private readonly decimal _monthlyDeposit = 0 m;public GiftCardAccount (string name, decimal initialBalance, decimal monthlyDeposit = 0 ) : base (name, initialBalance ) => _monthlyDeposit = monthlyDeposit;
只读字段用 readonly
修饰,只能在声明时初始化或在构造函数中赋值。
然后添加 PerformMonthEndTransactions
方法:
1 2 3 4 5 6 7 public override void PerformMonthEndTransactions (){ if (_monthlyDeposit != 0 ) { MakeDeposit(_monthlyDeposit, DateTime.Now, "余额充值" ); } }
给顶级语句添加代码进行调试:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 Console.WriteLine("----------" ); var giftCard = new GiftCardAccount("礼品卡1" , 100 , 50 );giftCard.MakeWithdrawal(20 , DateTime.Now, "买咖啡" ); giftCard.MakeWithdrawal(50 , DateTime.Now, "买杂货" ); giftCard.PerformMonthEndTransactions(); giftCard.MakeDeposit(27.50 m, DateTime.Now, "追加消费" ); Console.WriteLine(giftCard.GetAccountHistory()); var savings = new InterestEarningAccount("储蓄卡1" , 10000 );savings.MakeDeposit(750 , DateTime.Now, "存些钱" ); savings.MakeDeposit(1250 , DateTime.Now, "再存些钱" ); savings.MakeWithdrawal(250 , DateTime.Now, "支付每月账单" ); savings.PerformMonthEndTransactions(); Console.WriteLine(savings.GetAccountHistory());
由于信用卡允许余额为负,而原来的构造函数不允许初始余额小于零,我们可以在 BankAccount
类中定义一个只读字段 _minimunBalance
然后重载构造函数:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 private readonly decimal _minimumBalance;public BankAccount (string name, decimal initialBalance ) : this (name, initialBalance, 0 ) { }public BankAccount (string name, decimal initialBalance, decimal minimumBalance ){ Number = s_accountNumberSeed.ToString(); s_accountNumberSeed++; Owner = name; _minimumBalance = minimumBalance; if (initialBalance > 0 ) MakeDeposit(initialBalance, DateTime.Now, "初始余额" ); }
MakeWithdrawl
方法的 if
及条件也要改为
1 if (Balance - amount < _minimumBalance)
相应地,LineCreditAccount
类的构造函数也要改为
1 2 3 public LineOfCreditAccount (string name, decimal initialBalance, decimal creditLimit ) : base (name, initialBalance, -creditLimit ){ }
添加透支规则,让信用卡有偿透支,把原来的
1 2 3 4 5 6 7 8 9 10 11 12 13 public void MakeWithdrawal (decimal amount, DateTime date, string note ){ if (amount <= 0 ) { throw new ArgumentOutOfRangeException(nameof (amount), "Amount of withdrawal must be positive" ); } if (Balance - amount < _minimumBalance) { throw new InvalidOperationException("Not sufficient funds for this withdrawal" ); } var withdrawal = new Transaction(-amount, date, note); _allTransactions.Add(withdrawal); }
替换为
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 public void MakeWithdrawal (decimal amount, DateTime date, string note ){ if (amount <= 0 ) { throw new ArgumentOutOfRangeException(nameof (amount), "Amount of withdrawal must be positive" ); } Transaction? overdraftTransaction = CheckWithdrawalLimit(Balance - amount < _minimumBalance); Transaction? withdrawal = new (-amount, date, note); _allTransactions.Add(withdrawal); if (overdraftTransaction != null ) _allTransactions.Add(overdraftTransaction); } protected virtual Transaction? CheckWithdrawalLimit(bool isOverdrawn){ if (isOverdrawn) { throw new InvalidOperationException("Not sufficient funds for this withdrawal" ); } else { return default ; } }
protect
修饰符表示它只能在派生类中被调用。?
表示它允许为 null
。
并在 LineOfCreditAccount
类中添加
1 2 3 4 protected override Transaction? CheckWithdrawalLimit(bool isOverdrawn) => isOverdrawn ? new Transaction(-20 , DateTime.Now, "申请透支费用" ) : default ;
随后在顶级语句测试
1 2 3 4 5 6 7 8 var lineOfCredit = new LineOfCreditAccount("信用卡1" , 0 , 2000 );lineOfCredit.MakeWithdrawal(1000 m, DateTime.Now, "每月预支" ); lineOfCredit.MakeDeposit(50 m, DateTime.Now, "还点小钱" ); lineOfCredit.MakeWithdrawal(5000 m, DateTime.Now, "紧急维修资金" ); lineOfCredit.MakeDeposit(150 m, DateTime.Now, "部分修复" ); lineOfCredit.PerformMonthEndTransactions(); Console.WriteLine(lineOfCredit.GetAccountHistory());
此时各源文件的内容如下。
Program.cs
:
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 using CustomizedClasses;var account1 = new BankAccount("Tim" , 1000 ); Console.WriteLine($"用户 {account1.Number} 被 {account1.Owner} 初始化了 {account1.Balance} 的金额." ); var account2 = new BankAccount("Jane" , 2000 ); Console.WriteLine($"用户 {account2.Number} 被 {account2.Owner} 初始化了 {account2.Balance} 的金额." ); account1.MakeWithdrawal(300 , DateTime.Now, "买车票" ); Console.WriteLine($"{account1.Owner} 支出了" + account1.Balance); account1.MakeDeposit(100 , DateTime.Now, "补贴" ); Console.WriteLine($"{account1.Owner} 存入了" + account1.Balance); Console.WriteLine(account1.GetAccountHistory()); Console.WriteLine("----------" ); var giftCard = new GiftCardAccount("礼品卡1" , 100 , 50 );giftCard.MakeWithdrawal(20 , DateTime.Now, "买咖啡" ); giftCard.MakeWithdrawal(50 , DateTime.Now, "买杂货" ); giftCard.PerformMonthEndTransactions(); giftCard.MakeDeposit(27.50 m, DateTime.Now, "追加消费" ); Console.WriteLine(giftCard.GetAccountHistory()); var savings = new InterestEarningAccount("储蓄卡1" , 10000 );savings.MakeDeposit(750 , DateTime.Now, "存些钱" ); savings.MakeDeposit(1250 , DateTime.Now, "再存些钱" ); savings.MakeWithdrawal(250 , DateTime.Now, "支付每月账单" ); savings.PerformMonthEndTransactions(); Console.WriteLine(savings.GetAccountHistory()); Console.WriteLine("----------" ); var lineOfCredit = new LineOfCreditAccount("信用卡1" , 0 , 2000 );lineOfCredit.MakeWithdrawal(1000 m, DateTime.Now, "每月预支" ); lineOfCredit.MakeDeposit(50 m, DateTime.Now, "还点小钱" ); lineOfCredit.MakeWithdrawal(5000 m, DateTime.Now, "紧急维修资金" ); lineOfCredit.MakeDeposit(150 m, DateTime.Now, "部分修复" ); lineOfCredit.PerformMonthEndTransactions(); Console.WriteLine(lineOfCredit.GetAccountHistory());
BankAccount.cs
:
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 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 namespace CustomizedClasses ;public class BankAccount { private readonly decimal _minimumBalance; private static int s_accountNumberSeed = 1234567890 ; public string Number { get ; } public string Owner { get ; set ; } public virtual void PerformMonthEndTransactions () { } public decimal Balance { get { decimal balance = 0 ; foreach (var item in _allTransactions) { balance += item.Amount; } return balance; } } private List<Transaction> _allTransactions = new (); public BankAccount (string name, decimal initialBalance ) : this (name, initialBalance, 0 ) { } public BankAccount (string name, decimal initialBalance, decimal minimumBalance ) { Number = s_accountNumberSeed.ToString(); s_accountNumberSeed++; Owner = name; _minimumBalance = minimumBalance; if (initialBalance > 0 ) MakeDeposit(initialBalance, DateTime.Now, "初始余额" ); } public void MakeDeposit (decimal amount, DateTime date, string note ) { if (amount <= 0 ) { throw new ArgumentOutOfRangeException(nameof (amount), "Amount of deposit must be positive" ); } var deposit = new Transaction(amount, date, note); _allTransactions.Add(deposit); } public void MakeWithdrawal (decimal amount, DateTime date, string note ) { if (amount <= 0 ) { throw new ArgumentOutOfRangeException(nameof (amount), "Amount of withdrawal must be positive" ); } Transaction? overdraftTransaction = CheckWithdrawalLimit(Balance - amount < _minimumBalance); Transaction? withdrawal = new (-amount, date, note); _allTransactions.Add(withdrawal); if (overdraftTransaction != null ) _allTransactions.Add(overdraftTransaction); } protected virtual Transaction? CheckWithdrawalLimit(bool isOverdrawn) { if (isOverdrawn) { throw new InvalidOperationException("Not sufficient funds for this withdrawal" ); } else { return default ; } } public string GetAccountHistory () { var report = new System.Text.StringBuilder(); decimal balance = 0 ; report.AppendLine("日期\t\t交易\t余额\t备注" ); foreach (var item in _allTransactions) { balance += item.Amount; report.AppendLine($"{item.Date.ToShortDateString()} \t{item.Amount} \t{balance} \t{item.Notes} " ); } return report.ToString(); } } public class Transaction { public decimal Amount { get ; } public DateTime Date { get ; } public string Notes { get ; } public Transaction (decimal amount, DateTime date, string note ) { Amount = amount; Date = date; Notes = note; } }
InterestEarningAccount.cs
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 namespace CustomizedClasses ;public class InterestEarningAccount : BankAccount { public InterestEarningAccount (string name, decimal initialBalance ) : base (name, initialBalance ) { } public override void PerformMonthEndTransactions () { if (Balance > 500 m) { decimal interest = Balance * 0.02 m; MakeDeposit(interest, DateTime.Now, "按月放息" ); } } }
LineOfCreditAccount.cs
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 namespace CustomizedClasses ;public class LineOfCreditAccount : BankAccount { public LineOfCreditAccount (string name, decimal initialBalance, decimal creditLimit ) : base (name, initialBalance, -creditLimit ) { } public override void PerformMonthEndTransactions () { if (Balance < 0 ) { decimal interest = -Balance * 0.07 m; MakeWithdrawal(interest, DateTime.Now, "按月收息" ); } } protected override Transaction? CheckWithdrawalLimit(bool isOverdrawn) => isOverdrawn ? new Transaction(-20 , DateTime.Now, "申请透支费用" ) : default ; }
GiftCardAccount.cs
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 namespace CustomizedClasses ;public class GiftCardAccount : BankAccount { private readonly decimal _monthlyDeposit = 0 m; public GiftCardAccount (string name, decimal initialBalance, decimal monthlyDeposit = 0 ) : base (name, initialBalance ) => _monthlyDeposit = monthlyDeposit; public override void PerformMonthEndTransactions () { if (_monthlyDeposit != 0 ) { MakeDeposit(_monthlyDeposit, DateTime.Now, "余额充值" ); } } }
四、深入理解继承 4.1 继承的概念 继承,就是派生类对基类的延续。C#
只支持单一继承。
继承就是继承成员,除了静态构造函数、实例构造函数、终结器。
继承的成员是否可见取决于它们的可访问性,如嵌套派生类才可以访问基类的私有成员,就像:
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 public class A { private int _value = 10 ; public class B : A { public int GetValue () { return _value; } } } public class C : A { } public class AccessExample { public static void Main (string [] args ) { var b = new A.B(); Console.WriteLine(b.GetValue()); } }
继承通常需要方法重写,方法重写就是在派生类用 override
修饰的方法重写基类中的虚方法,虚方法可以被 virtual
、override
或 abstract
修饰。virtual
允许方法被重写,而 abstract
要求派生类必须重写该方法。
1 2 3 4 5 6 7 8 9 10 11 12 public abstract class A { public abstract void Method1 () ; } public class B : A { public void Method3 () { } }
4.2 隐式继承 定义一个空类:
1 2 public class SimpleClass { }
其实这个空类已经有九个成员,是因为它已经隐式继承了 Object
类。
4.3 继承表示 “is a” 继承表示 “is a”,也就是派生类是基类的一个特定版本。就像香蕉 “is a” 水果。
4.4 设计抽象基类 许多情况下不希望基类提供实现代码,而是用于声明抽象方法,这时它就是个抽象基类。但由于实例化抽象基类无意义,此时用 abstract
修饰该类。
就像下面的 Shape
抽象基类,提供面积和周长两个属性:
1 2 3 4 5 6 7 8 9 10 11 12 public abstract class Shape { public abstract double Area { get ; } public abstract double Perimeter { get ; } public override string ToString () => GetType().Name; public static double GetArea (Shape shape ) => shape.Area; public static double GetPerimeter (Shape shape ) => shape.Perimeter; }
给它派生为三种图形:
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 51 52 53 54 55 56 57 using System;public class Square : Shape { public Square (double length ) { Side = length; } public double Side { get ; } public override double Area => Math.Pow(Side, 2 ); public override double Perimeter => Side * 4 ; public double Diagonal => Math.Round(Math.Sqrt(2 ) * Side, 2 ); } public class Rectangle : Shape { public Rectangle (double length, double width ) { Length = length; Width = width; } public double Length { get ; } public double Width { get ; } public override double Area => Length * Width; public override double Perimeter => 2 * Length + 2 * Width; public bool IsSquare () => Length == Width; public double Diagonal => Math.Round(Math.Sqrt(Math.Pow(Length, 2 ) + Math.Pow(Width, 2 )), 2 ); } public class Circle : Shape { public Circle (double radius ) { Radius = radius; } public override double Area => Math.Round(Math.PI * Math.Pow(Radius, 2 ), 2 ); public override double Perimeter => Math.Round(Math.PI * 2 * Radius, 2 ); public double Circumference => Perimeter; public double Radius { get ; } public double Diameter => Radius * 2 ; }
执行它。
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 using System;public class Example { public static void Main () { Shape[] shapes = { new Rectangle(10 , 12 ), new Square(5 ), new Circle(3 ) }; foreach (Shape shape in shapes) { Console.WriteLine($"{shape} : area, {Shape.GetArea(shape)} ; " + $"perimeter, {Shape.GetPerimeter(shape)} " ); if (shape is Rectangle rect) { Console.WriteLine($" Is Square: {rect.IsSquare()} , Diagonal: {rect.Diagonal} " ); continue ; } if (shape is Square sq) { Console.WriteLine($" Diagonal: {sq.Diagonal} " ); continue ; } } } }