C# 程式基礎:物件
C# 是很好的物件導向語言,而且微軟的 .NET Framework 函式庫設計得相當優美,這使得 C# 的魅力相當大。
傳統的程式設計會將資料與程式分開,但是在物件導向的概念當中,資料與程式被合併成一個結構,這個結構就稱為物件。
一個物件可以包含資料部分 (資料成員) 與函數部分 (函數成員),函數成員可以對資料成員進行操作,以下是一個 C# 的物件範例,該範例中定義了一個人員 (Person1) 的結構,該結構包含兩個資料成員 (name, weight) 與一個成員函數 (checkWeight),該函數會檢查人員結構的體重 (weight) 看看是重還是輕。另外,還包含了一個建構函數 Person1() ,這個建構函數可以讓使用者在建立物件時順便將參數傳入,這是物件導向的一種常見手法。
using System;
class Object1 {
public static void Main(String[] args) {
Person1 p1, p2;
p1 = new Person1("大雄", 50);
p2 = new Person1("胖虎", 80);
p1.checkWeight();
p2.checkWeight();
p2.weight = 68;
p1.checkWeight();
p2.checkWeight();
}
}
class Person1 {
public string name;
public int weight;
public Person1(string pName, int pWeight) {
name = pName;
weight = pWeight;
}
public void checkWeight()
{
Console.Write(name+"體重 "+weight+" 公斤,");
if (weight < 70)
Console.WriteLine("很苗條!");
else
Console.WriteLine("很穩重!");
}
}
執行結果
D:\myweb\teach\CSharpProgramming>csc Object1.cs
Microsoft (R) Visual C# 2008 Compiler version 3.5.30729.1
for Microsoft (R) .NET Framework version 3.5
Copyright (C) Microsoft Corporation. All rights reserved.
D:\myweb\teach\CSharpProgramming>Object1
大雄體重 50 公斤,很苗條!
胖虎體重 80 公斤,很穩重!
大雄體重 50 公斤,很苗條!
胖虎體重 68 公斤,很苗條!
封裝
在傳統的結構化程式設計 (像是 C, Fortran, Pascal) 當中,我們用函數來處理資料,但是函數與資料是完全區隔開來的兩個世界。然而,在物件導向當中,函數與資料被合為一體,形成一種稱為物件的結構,我們稱這種將函數與資料放在一起的特性為「封裝」。
以下我們將以矩形物件為範例,說明 C# 當中的封裝語法,以及物件導向中的封裝概念。
範例一:封裝 – 將函數與資料裝入物件當中
using System;
class Rectangle
{
double width, height;
double area()
{
return width * height;
}
public static void Main(string[] args)
{
Rectangle r = new Rectangle();
r.width = 5.0;
r.height = 8.0;
Console.WriteLine("r.area() = " + r.area());
}
}
執行結果
r.area() = 40
修改進化版
由於上述物件與主程式混在一起,可能容易造成誤解,因此我們將主程式獨立出來,並且新增了一個 r2 的矩形物件,此時程式如下所示。
using System;
class Rectangle
{
public double width, height;
public double area()
{
return width * height;
}
}
class Test
{
public static void Main(string[] args)
{
Rectangle r = new Rectangle();
r.width = 5.0;
r.height = 8.0;
Console.WriteLine("r.area() = " + r.area());
Rectangle r2 = new Rectangle();
r2.width = 7.0;
r2.height = 4.0;
Console.WriteLine("r2.area() = " + r2.area());
}
}
執行結果:
r.area() = 40
r2.area() = 28
範例二:加上建構函數
using System;
class Rectangle
{
double width, height;
public Rectangle(double w, double h)
{
width = w;
height = h;
}
public double area()
{
return width * height;
}
public static void Main(string[] args)
{
Rectangle r = new Rectangle(5.0, 8.0);
Console.WriteLine("r.area() = " + r.area());
}
}
執行結果
r.area() = 40
範例二:加上建構函數 – 同時有 0 個與兩個引數
using System;
class Rectangle
{
public double width, height;
public Rectangle() { }
public Rectangle(double w, double h)
{
width = w;
height = h;
}
public double area()
{
return width * height;
}
}
class Test
{
public static void Main(string[] args)
{
Rectangle r = new Rectangle(5.0, 8.0);
Console.WriteLine("r.area() = " + r.area());
Rectangle r2 = new Rectangle();
r2.width = 7.0;
r2.height = 4.0;
Console.WriteLine("r2.area() = " + r2.area());
}
}
習題:向量物件
using System;
class Vector
{
double[] a;
public Vector(double[] array)
{
a = new double[array.GetLength(0)];
for (int i = 0; i < a.GetLength(0); i++)
{
a[i] = array[i];
}
}
public Vector add(Vector v2)
{
Vector rv = new Vector(v2.a);
for (int i = 0; i < rv.a.GetLength(0); i++)
{
rv.a[i] = this.a[i] + v2.a[i];
}
return rv;
}
public void print()
{
for (int i = 0; i < a.GetLength(0); i++)
{
Console.Write(a[i] + " ");
}
Console.WriteLine();
}
}
class Test
{
public static void Main(string[] args)
{
Vector v1 = new Vector(new double[] { 1.0, 2.0, 3.0 });
Vector v2 = new Vector(new double[] { 4.0, 5.0, 6.0 });
Vector v3 = v1.add(v2);
v1.print();
v2.print();
v3.print();
}
}
進階練習 1:加上內積的函數,並寫出呼叫範例。 進階練習 2:寫出矩陣物件 (有加法、乘法) (方法一:用二維陣列、方法二:用 Vector)。
習題:矩陣物件
繼承
範例一:矩形、圓形繼承形狀 (Shape)
using System;
class Shape
{
public virtual double area() { return 0.0; }
}
class Rectangle : Shape
{
double width, height;
public Rectangle(double w, double h)
{
width = w;
height = h;
}
public override double area()
{
return width * height;
}
}
class Circle : Shape
{
double r;
public Circle(double r)
{
this.r = r;
}
public override double area()
{
return 3.1416 * r * r;
}
}
class Test
{
public static void Main(string[] args)
{
Shape s = new Shape();
Console.WriteLine("s.area() = " + s.area());
Rectangle r = new Rectangle(5.0, 8.0);
Console.WriteLine("r.area() = " + r.area());
Circle c = new Circle(2);
Console.WriteLine("c.area() = " + c.area());
}
}
執行結果:
s.area() = 0
r.area() = 40
c.area() = 12.5664
範例二:加入厚度與體積函數 (在子物件使用父物件的欄位)
執行結果
s.area() = 0
r.area() = 40
r.volume() = 80
c.area() = 12.5664
c.volume() = 37.6992
c.volume() = 62.832
程式碼
using System;
class Shape
{
public double thick;
public virtual double area() { return 0.0; }
}
class Rectangle : Shape
{
double width, height;
public Rectangle(double w, double h)
{
width = w;
height = h;
thick = 2.0;
}
public override double area()
{
return width * height;
}
public double volume()
{
return width * height * thick;
}
}
class Circle : Shape
{
double r;
public Circle(double r)
{
this.r = r;
thick = 3.0;
}
public override double area()
{
return 3.1416 * r * r;
}
public double volume()
{
return area() * thick;
}
}
class Test
{
public static void Main(string[] args)
{
Shape s = new Shape();
Console.WriteLine("s.area() = " + s.area());
Rectangle r = new Rectangle(5.0, 8.0);
Console.WriteLine("r.area() = " + r.area());
Console.WriteLine("r.volume() = " + r.volume());
Circle c = new Circle(2);
Console.WriteLine("c.area() = " + c.area());
Console.WriteLine("c.volume() = " + c.volume());
c.thick = 5;
Console.WriteLine("c.volume() = " + c.volume());
}
}
範例三:將 volume() 函數提到 Shape 物件中
using System;
class Shape
{
public double thick;
public virtual double area() { return 0.0; }
public double volume()
{
return area() * thick;
}
}
class Rectangle : Shape
{
double width, height;
public Rectangle(double w, double h)
{
width = w;
height = h;
thick = 2.0;
}
public override double area()
{
return width * height;
}
}
class Circle : Shape
{
double r;
public Circle(double r)
{
this.r = r;
thick = 3.0;
}
public override double area()
{
return 3.1416 * r * r;
}
}
class Test
{
public static void Main(string[] args)
{
Shape s = new Shape();
Console.WriteLine("s.area() = " + s.area());
Rectangle r = new Rectangle(5.0, 8.0);
Console.WriteLine("r.area() = " + r.area());
Console.WriteLine("r.volume() = " + r.volume());
Circle c = new Circle(2);
Console.WriteLine("c.area() = " + c.area());
Console.WriteLine("c.volume() = " + c.volume());
c.thick = 5;
Console.WriteLine("c.volume() = " + c.volume());
}
}
習題:人、學生與老師
請定義「人、學生與老師」等三個類別,其中的人有「姓名、性別、年齡」三個欄位,學生另外有「學號、年級」等兩個欄位,老師另外有「職等」的欄位,所有物件都有 print() 函數,可以將該物件的所有欄位印出。
請建立一個具有 3 個學生與兩個老師的陣列,利用多型機制,呼叫 print 函數以便印出這 5 個人的基本資料,如下所示。
學生 -- 姓名:王小明,性別:男,年齡:20,學號:R773122456,年級:一年級
學生 -- 姓名:李小華,性別:女,年齡:19,學號:R773122432,年級:一年級
教師 -- 姓名:陳福氣,性別:男,年齡:40,職等:教授
學生 -- 姓名:黃大虎,性別:男,年齡:22,學號:R773122721,年級:四年級
教師 -- 姓名:李美女,性別:女,年齡:35,職等:助理教授
解答:
using System;
using System.Collections.Generic;
using System.Text;
namespace Person
{
/*
* 請定義「人、學生與老師」等三個類別,其中的人有「姓名、性別、年齡」三個欄位,
* 學生另外有「學號、年級」等兩個欄位,老師另外有「職等」的欄位,所有物件都有
* print() 函數,可以將該物件的所有欄位印出。
*
* 請建立一個具有 3 個學生與兩個老師的陣列,利用多型機制,呼叫 print 函數以便
* 印出這 5 個人的基本資料,如下所示。
*
* 學生 -- 姓名:王小明,性別:男,年齡:20,學號:R773122456,年級:一年級
* 學生 -- 姓名:李小華,性別:女,年齡:19,學號:R773122432,年級:一年級
* 教師 -- 姓名:陳福氣,性別:男,年齡:40,職等:教授
* 學生 -- 姓名:黃大虎,性別:男,年齡:22,學號:R773122721,年級:四年級
* 教師 -- 姓名:李美女,性別:女,年齡:35,職等:助理教授
*/
class Program
{
static void Main(string[] args)
{
Student s1 = new Student("王小明", "男", 20, "R773122456", 1);
Student s2 = new Student("李小華", "女", 19, "R773122432", 1);
Student s3 = new Student("黃大虎", "男", 22, "R773122721", 4);
Person t1 = new Person("陳福氣", "男", 40);
Person t2 = new Person("李美女", "女", 35);
Person[] list = { s1, s2, t1, s3, t2 };
foreach (Person p in list)
{
p.print();
Console.WriteLine();
}
}
}
class Person
{
String name;
String sex;
int age;
public Person(String name, String sex, int age)
{
this.name = name;
this.sex = sex;
this.age = age;
}
public virtual Person print()
{
Console.Write("姓名:"+name+" 性別:"+sex+" 年齡:"+age);
return this;
}
}
class Student : Person
{
String id;
int degree;
public Student(String name, String sex, int age, String id, int degree)
: base(name, sex, age)
{
this.id = id;
this.degree = degree;
}
public override Person print()
{
Console.Write("學生 -- ");
base.print();
Console.Write(" 學號:" + id + " 年級:" + degree);
return this;
}
}
}
多型
物件導向的多型機制,是指當兩個以上的類別繼承同一種父類別時,我們可以用父類別型態容納子類別的物件,真正進行函數呼叫時會呼叫到子類別的函數,此種特性稱之為多型。
以下是我們用 C# 實作的一個多型範例,在範例中,我們宣告了一個形狀類別,該類別具有一個 area() 函數可以計算該形狀的面積,然後我們又宣告了兩個子類別 Rectangle (矩形) 與 Circle (圓形)。我們將兩者放入到 shapes 陣列中,以便展示多型技巧,用父類別容器呼叫子類別的實體。
範例:形狀、矩形與圓形
using System;
class Shape
{
public virtual double area() { return 0.0; }
public static void Main(string[] args)
{
Rectangle r = new Rectangle(5.0, 8.0);
Console.WriteLine("r.area() = " + r.area());
Circle c = new Circle(3.0);
Console.WriteLine("c.area() = " + c.area());
Shape[] shapes = { r, c };
for (int i = 0; i < shapes.Length; i++)
Console.WriteLine("shapes[" + i + "].area()=" + shapes[i].area());
}
}
class Rectangle : Shape
{
double width, height;
public Rectangle(double w, double h)
{
width = w;
height = h;
}
public override double area()
{
return width * height;
}
}
class Circle : Shape
{
double r;
public Circle(double r)
{
this.r = r;
}
public override double area()
{
return 3.1416 * r * r;
}
}
執行結果
r.area() = 40
c.area() = 28.2744
shapes[0].area()=40
shapes[1].area()=28.2744
範例:使用抽象父型態
using System;
class ShapeTest
{
public static void Main(string[] args)
{
Rectangle r = new Rectangle(5.0, 8.0);
Console.WriteLine("r.area() = " + r.area());
Circle c = new Circle(3.0);
Console.WriteLine("c.area() = " + c.area());
Shape[] shapes = { r, c };
for (int i = 0; i < shapes.Length; i++)
Console.WriteLine("shapes[" + i + "].area()=" + shapes[i].area());
}
}
abstract class Shape
{
public abstract double area();
}
class Rectangle : Shape
{
double width, height;
public Rectangle(double w, double h)
{
width = w;
height = h;
}
public override double area()
{
return width * height;
}
}
class Circle : Shape
{
double r;
public Circle(double r)
{
this.r = r;
}
public override double area()
{
return 3.1416 * r * r;
}
}
執行結果:
r.area() = 40
c.area() = 28.2744
shapes[0].area()=40
shapes[1].area()=28.2744
範例:使用介面
using System;
class ShapeTest
{
public static void Main(string[] args)
{
Rectangle r = new Rectangle(5.0, 8.0);
Console.WriteLine("r.area() = " + r.area());
Circle c = new Circle(3.0);
Console.WriteLine("c.area() = " + c.area());
Shape[] shapes = { r, c };
for (int i = 0; i < shapes.Length; i++)
Console.WriteLine("shapes[" + i + "].area()=" + shapes[i].area());
}
}
interface Shape
{
double area();
}
class Rectangle : Shape
{
double width, height;
public Rectangle(double w, double h)
{
width = w;
height = h;
}
public double area()
{
return width * height;
}
}
class Circle : Shape
{
double r;
public Circle(double r)
{
this.r = r;
}
public double area()
{
return 3.1416 * r * r;
}
}
執行結果:
r.area() = 40
c.area() = 28.2744
shapes[0].area()=40
shapes[1].area()=28.2744
範例:較完整複雜的版本
using System;
class Shape
{
public double thick = 0.0;
public virtual double area() { return 0.0; }
public double volume()
{
return area() * thick;
}
public override String ToString()
{
return "Shape: thick=" + thick+" area="+area()+" volume="+volume();
}
}
class Rectangle : Shape
{
double width, height;
public Rectangle(double w, double h)
{
width = w;
height = h;
thick = 2.0;
}
public override String ToString()
{
return base.ToString()+" Rectangle:width=" + width + " height=" + height;
}
public override double area()
{
return width * height;
}
}
class Circle : Shape
{
double r;
public Circle(double r)
{
this.r = r;
thick = 3.0;
}
public override String ToString()
{
return base.ToString() + " Circle:r=" + r + " thick=" + thick;
}
public override double area()
{
return 3.1416 * r * r;
}
}
class Test
{
public static void Main(string[] args)
{
Shape s = new Shape();
Console.WriteLine("s.area() = " + s.area());
Rectangle r = new Rectangle(5.0, 8.0);
Console.WriteLine("r.area() = " + r.area());
Console.WriteLine("r.volume() = " + r.volume());
Circle c = new Circle(2);
Console.WriteLine("c.area() = " + c.area());
Console.WriteLine("c.volume() = " + c.volume());
c.thick = 5;
Console.WriteLine("c.volume() = " + c.volume());
Shape[] array = new Shape[] { s, r, c, r };
foreach (Shape o in array)
{
// Console.WriteLine("o.area()=" + o.area() +" o.volume()="+o.volume());
Console.WriteLine(o.ToString());
}
}
}