iT邦幫忙

第 12 屆 iThome 鐵人賽

DAY 12
2

到底什麼是物件?

在物件導向的世界裡面,什麼東西都是物件

物件裡面會包含著該物件的屬性,方法等等相關的程式碼
當今天你宣告出一個物件時,你不必再去為他設計方法或添加屬性等等,因為那是物件本身的功能

還記得當初我們在java寫過的這段嗎?

public static int h2t(String input){
    int output = 0;
    int i = 0;
    while (i < input.length() ) {

我們有一個名字為input的字串,當我們宣告他時就等於製作了一個字串的物件
此時我們不必再想辦法如何找出字串的長度,因為字串這個物件已經提供了找出自己長度的方法

input.length()

中間的 . 是為了區隔出變數名稱跟從屬於他的屬性或方法

同樣的道理在C#也通

static void h2t(String input){
    double output = 0;
    var pow = input.Length;

不過C#的屬性提供的不是方法而是屬性
同樣中間使用 . 來取用字串的屬性Length

這樣子做有什麼好處嗎?

這樣子做可以確保程式碼的正確性

否則如果你給字串一取得長度的方法length()好了,你會這樣寫

length(input)

如果你今天在方法的輸入值丟入方法會怎樣?
會直接炸掉
因為方法沒有長度

那在輸入值的時候限定型別不就好了?

這樣會讓你的方法難以拓展
如果你今天想要一個陣列的長度該怎麼做?

arrayLength(input)

這樣嗎?

之後想要為其他的型別設計一個取得長度的方法就必須要找一個沒用過的名字
但是明明都是要取得長度,為什麼不直接用同一個名字就好?

用物件就可以解決這個問題
方法由物件自己提供而不是語言提供
所以方法是一定可以執行的

這還只是物件的其中一個優點,讓我們再深入一點體會吧

我們來寫自己的物件

開一個新的工作目錄給C#
然後執行

dotnet new console -n classType

記得不要偷懶名稱只打class,因為你在這裡設定的名稱會被套用在專案產生的namespace

點開你的Program.cs檔,看看熟悉的內容

using System;

namespace classType  //看到你剛才打的專案名稱了吧
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("Hello World!");
        }
    }
}

我們先把裡面的內容給補齊

static void Main(string[] args)
{
    Console.WriteLine("Tell me what you want to do:");
    Console.WriteLine("(1)T to H (2)H to T");
    String choose = Console.ReadLine();
    switch (choose)
    {  
        case "1":
            Console.WriteLine("Please enter the number:");
            var t2h = new t2h(Console.ReadLine());
            t2h.convert();
            break;
        case "2":
            Console.WriteLine("Please enter the number");
            var h2t= new h2t(Console.ReadLine());
            h2t.convert();
            break;
        default:
            Console.WriteLine("Wrong selection");
            break;
    }
    Main();
}

前面都很熟悉了

看到這裡

var t2h = new t2h(Console.ReadLine());

new是建立物件的關鍵字,這裡建立了一個型別為t2h的物件,初始化的值就是我們的輸入值

t2h的型別? 根本聽都沒聽過阿

當然沒聽過,因為我們來沒建

在跟Program.cs的同一層目錄建立一個檔案,名稱叫做

t2h.cs

(這下你知道為什麼副檔名是cs的原因了吧)

然後貼上程式碼

namespace classType
{
    class t2h
    {
        private string input;

        public t2h(string input){
            this.input = input;
        }        

        public void convert(){

        }
        private string returnAE(int number){
            
        }
    }   
}

注意namespace必須要跟你的Program的相同,如此一來才能處於同一個命名空間
你的Main方法才能正確無誤的使用t2h的class

class是物件的關鍵字,使用這個關鍵字可以建立一個物件的藍圖,
之後所有使用new建立的物件都會按照這張藍圖來做,並且會有這張藍圖的屬性跟方法

建構子

注意到個跟class同名的方法

public t2h(string input){
    this.input = input;
}        

這個稱為建構子
目的在於為你的物件建立帶入一個初始值
注意建構子並不是所有語言都有,不過學了也不吃虧,就先記著吧

還記得我們剛才的

var t2h = new t2h(Console.ReadLine());

這時就會將Console.ReadLine()給帶入到建構子的input中
注意this.input = input這一行中的兩個input不一樣喔

https://ithelp.ithome.com.tw/upload/images/20200912/201278363jOjf6Yndw.png

為什麼要帶入參數後再把他丟到同名的屬性內繞一大圈?

因為有可能我們會對建構子的參數動手腳
比方說我們希望存下來的參數就已經是轉型過的數值

public t2h(string input){
    this.input = Int32.Parse(input);
}        

就可以這樣寫(而且這樣寫還比較好)
只是我希望重點說明建構子的變數名稱跟物件的屬性名稱可以相同,才故意這樣設計

屬性

什麼是物件的屬性?

就是這個

private string input;

private表示我們對外隱藏這個屬性,只有物件本身可以看得到他
這樣的好處是我們不會誤用

t2h.input = 120

之類的不小心修改到這個屬性

為什麼要特地這樣做? 小心不要改到不就好了?

因為你寫的不一定是程式,也有可能包裝成函式庫給別人用
但是別人不一定知道input這個屬性是不能隨意更改的,所以乾脆使用private將他隱藏起來

方法

而由於方法我們希望公開給外面的人使用,像這樣

t2h.convert()

因此我們就會使用public這個關鍵字

當然,也會有像是returnAE這種不想要公開的方法
我們可以使用private隱藏方法

private string returnAE(int number){

畢竟外面的人想要使用t2h.returnAE必須對這隻程式有全盤的了解才行

了解方法的宣告之後我們可以填充我們的方法了
先看看convert()吧

public void convert(){
    try
    {
        var num = Int32.Parse(input);
        for (int i = 0; i < 16; i++)
        {
            for (int j = 0; j < 16; j++)
            {
                for (int k = 0; k < 16; k++)
                {
                    if (Math.Pow(16,2)*i+Math.Pow(16,1)*j+Math.Pow(16,0)*k == num)
                    {
                        Console.WriteLine("{0}{1}{2}",returnAE(i),returnAE(j),returnAE(k));
                    }   
                }   
            }   
        }
    }
    catch (System.Exception)
    {
        Console.WriteLine("Wrong Input");
    }
}

差別只在於這裡

public void convert(){

注意這時我們的convert是沒有輸入值的喔
因為剛才使用建構子時我們就已經取得輸入,並且放入物件的屬性了

這時我們只需要取出來用就行

var num = Int32.Parse(input); //取用屬性的input

為什麼要先放入到屬性再取出來? 不覺得多此一舉嗎?

理由在於這樣我們可以確保程式的運作性
畢竟一個沒有輸入值的convert根本沒有運作的必要
因此我們在建構這個物件(也就是使用建構子)時要求一定要帶入參數,當作input

這樣我們在使用convert時就不必擔心要不要帶入參數或是型別是什麼了,
物件會自己去找自己的屬性

不是說物件的屬性必須要用this指定嗎?

這時候由於這個方法內沒有同樣稱為input的變數,因此我們不必擔心重名的問題
就可以不必使用this(當然你要用也是可以,不會報錯)

物件有一個原則是會找離自己最近的

public void convert(){
    try
    {
        var input = "12";
        var num = Int32.Parse(input); //這時input就會取用方法內部的input而不是較外層的屬性input
        for (int i = 0; i < 16; i++)
        {

而另外一個方法AEreturn由於是我們寫出來給t2h用的轉換工具,因此長得跟我們當初寫的一樣不必修改

這時候你可以自己再建立一個h2t的cs檔,並在裡面寫物件的程式碼
都完成之後可以到這裡對一下答案
當然,如果你程式可以正常運作就不必對答案了,一定是對的
也不必擔心說如果跟我寫的不一樣該怎麼辦,程式本來就有無限多種寫法
只要運作得宜就沒問題

物件的使用

這時候回來看我們的Main方法吧

case "1":
    Console.WriteLine("Please enter the number:");
    var t2h = new t2h(Console.ReadLine()); //這裡使用了建構子建立物件
    t2h.convert();  //這裡取用物件的方法來做使用
    break;
case "2":
    Console.WriteLine("Please enter the number");
    var h2t= new h2t(Console.ReadLine());
    h2t.convert();
    break;
default:
    Console.WriteLine("Wrong selection");
    break;

跟單純使用方法不使用物件的方式

String number = Console.ReadLine();  //先將讀取到的數值丟數變數內
t2h(number); //再把變數的值丟入方法內

感覺沒什麼差別嘛

差別在於可以減少變數的使用跟搬運
也不用擔心t2h這個方法到底要帶什麼變數,變數的型別又該是什麼
當然,這些問題在專案都還小時是看不出來的
因此在小型專案使用物件都可以說是殺雞焉用牛刀
我們之前寫的時候也不需要物件的概念就把程式寫出來了對吧
但是你不會這輩子都只寫小專案,所以學會使用物件也不會有損失

同場加映python物件

我們來看看python是怎麼寫物件的吧

class t2h:
    def __init__(self, input):
        self.__input = int(input)

    def convert(self):
        for i in range(16):
            for j in range(16):
                for k in range(16):
                    if math.pow(16,2)*i+math.pow(16,1)*j+math.pow(16,0)*k == self.__input:
                        print(self.__returnAE(i)+self.__returnAE(j)+self.__returnAE(k))
    
    def __returnAE(self, input):
            if input<10:
                return str(input)
            elif input==10:
                return "A"
            elif input==11:
                return "B"
            elif input==12:
                return "C"
            elif input==13:
                return "D"
            elif input==14:
                return "E"
            elif input==15:
                return "F"
            else:
                return "wrong"

關鍵字一樣叫class,而且跟往常一樣不使用{}

建構子

python的建構子一律叫__init__

def __init__(self, input):
    self.__input = int(input)

變數名稱前面加上__表示私有變數

同樣道理

def convert(self): # 外面可以取用的方法
def __returnAE(self, input): # 對外部隱藏的方法

記得只要是物件的方法都需要帶入self這個參數
因此取用內部方法的寫法是這樣

self.__returnAE(i)

這樣你應該就可以自己照著寫出h2t的物件了

物件的使用

if choose== "1":
    number = input("Please enter the number\n")
    t2h(number).convert() # 這一行等於直接建立了物件,並且同時使用物件的方法
elif choose == "2":
    number = input("Please enter the number\n")
    change = h2t(number)
    change.convert()
else:
    print("Wrong select")

為什麼python不用new就可以建立物件?

因為python的物件沒有動靜態之分,但是C#有
這是一個比較深入的概念,詳細可以看這裡

小結

物件有三大概念

  • 封裝
  • 繼承
  • 多型
    今天我們只大概說了封裝,因為封裝最常用到所以也很方便可以寫出範例
    之後各位有興趣可以繼續深入研究繼承與多型(就算沒有興趣也還是要深入研究)

封裝的概念大概有兩點

  • 公開與私有成員(屬性與方法之類的)
  • 將屬性與屬性相關的方法包裝在一個物件內

之前看過一篇文章在想使用物件時寫程式的效率,大概是在說如果程式碼小於100行,那就不須要使用物件
而我們的程式大約就在100行左右,因此用不用物件看起來是差不多的

現在大概都沒有沒有物件導向概念的語言了(主流剩下C沒有),有些原本沒有支援的現在也有支援了
就算使用了物件導向的語言,也只是代表這個語言提供的變數等等都是物件,並不代表你寫出來的程式有物件的概念

今天是中場休息沒有學語言,明天我們回到正題
開始我們來介紹排名沒那麼前面卻又很重要的語言們吧
首先就是我最喜歡的語言GO

恩,沒錯,這門語言就叫GO
不,這不是個冷笑話


上一篇
C/C++ 如果有朋友建議你程式語言先學C/C++,我建議你立刻跟他絕交
下一篇
golang 為網路而生的Google親兒子
系列文
你會十五種程式語言?不,我會十五種HelloWorld.為了避免這種狀況,因此寫了這篇:淺入淺出十五種程式語言30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

1 則留言

0
ultrahsin
iT邦新手 5 級 ‧ 2021-06-05 22:56:40

非常感謝作者的好文分享。

這邊按照文章實跑C#部分,
有一些小發現:

  1. t2h.cs 和 h2t.cs皆要加using System; 才不會建置失敗
  2. Program.cs裡作者在Main方法最後有一行Main();
    一樣也要刪掉後執行,才不會建置失敗

我要留言

立即登入留言