iT邦幫忙

2019 iT 邦幫忙鐵人賽

DAY 30
0

前言

本文將介紹.Net界堪稱神級的工具 - Resharper
先強調這絕對不是業配文!
筆者也沒有收取廠商任何廣告費用,
只是希望讓更多人認識這套工具。

同步發表於個人點部落 - [鐵人賽Day30] ASP.Net Core MVC 進化之路 - 工具篇(4) / Refactor with ReSharper

ReSharper

ReSharperJetBrains公司發行的一套Visual Studio擴充套件
提供的擴充功能很多,
以下僅摘述筆者比較常用的功能介紹:

  • 原始碼查看
  • 程式碼靜態分析
  • 快速重構

Price

單純購買擴充套件的話一年需299美元(約9200$NT),
授權計價皆以為單位,
但續約的價格會相對便宜許多(計價方案如下圖 - 2018.11.14)。

沒使用過的朋友可以到官網下載30天的試用版
下載之後進行安裝即可(需先安裝Visual Studio 2017)。

原始碼查看

雖然現在有非常多的套件都是Open Source,
但看個程式碼還要大費周章跑到GitHub總是為令人感到麻煩,
ReSharper整合了自己家的DotPeek(一套免費.Net原始碼查看工具),
讓原始碼查看(快捷鍵F12)功能變的相當輕易,
但真的去用的人還是占少數。

你也許會想:

「Code都寫不完了,哪有時間看原始碼?」
「沒事看原始碼,吃飽太閒嗎?」
「功能好好的,開發也沒問題,幹嘛看?」

那換個情境來思考,

假設你帶你的小孩看完醫生後去領藥,
你不會關注藥物過敏或副作用嗎?

同樣的場景,換成了你的程式碼,
我們卻只希望「會動就好」。

我們太習慣「從結果推斷過程」的學習方式,
久了就容易不小心地把錯誤的認知當成「常識」

舉簡單的資料結構而言,

請問以下的程式碼誰執行會比較快?

List<string> stringList = GetStringList();
stringList.LastOrDefault();

string[] stringArray = GetStringArray();
stringArray.LastOrDefault();

以直覺的思考方式來說,
陣列是以索引值取得集合中的元素,
一般串列則只會記得下一個元素的記憶體位置。

所以你就會很自然的推想:
陣列是用Array[陣列長度-1]的方式取得最後一筆元素的值,
串列則會逐筆尋找,直到沒有下一個元素為止。
再加上使用了最熟也最不熟的LINQ

就會很自然的認為兩者的效能差距應該會非常大(直接找跟逐筆找的差別)。
但真的是這樣嗎?
我們使用原始碼查看功能來看一下LastOrDefault這段程式碼。

public static TSource LastOrDefault<TSource>(this IEnumerable<TSource> source) {
    if (source == null) throw Error.ArgumentNull("source");
    IList<TSource> list = source as IList<TSource>;
    if (list != null) {
        int count = list.Count;
        if (count > 0) return list[count - 1];
    }
    else {
        using (IEnumerator<TSource> e = source.GetEnumerator()) {
            if (e.MoveNext()) {
                TSource result;
                do {
                    result = e.Current;
                } while (e.MoveNext());
                return result;
            }
        }
    }
    return default(TSource);
}

程式內容翻譯概述如下:

LastOrDefault是針對IEnumerable<TSource>做擴充,
當被呼叫時會先嘗試轉型為IList<TSource>的型別,
如果轉型成功就使用[長度-1]的方式回傳元素值,
不能轉型的話才會逐筆呼叫MoveNext() 。

當然,我們不可能有時間把每個API看過一遍,
但在使用API得到非預期的結果時,
查看原始碼總比你瞎猜來的好。

程式碼靜態分析

這項功能會自動分析我們現有的程式碼,
然後建議我們比較好的寫法。
舉個簡單的範例,
假設我的原始碼長這樣:

var arr = new int[]{ 3, 4, 5, 6, 7 };
var sum = 0;
for (int i=0; i< arr.Length; i++)
{
    sum += arr[i];
}

這時ReSharper會很貼心的提醒你。

除了提醒之外,
還可以快速幫你重構。

選擇轉換成LINQ寫法,
一秒幫你轉過去。

按按快捷鍵事情就做完了,
而且還比較好看!
有關程式碼分析的部分有興趣的讀者可以參考官網的介紹。

快速重構

接下來要介紹的是ReSharper的靈魂所在 - Refactor(重構)
以下為Martin Fowler大師對重構的定義:

在不改變軟體外部行為的前提下,改變其內部結構,使其更容易理解且易於修改

重構牽涉的範圍之廣,
推薦大家可以閱讀聖經本重構─改善既有程式的設計
此書介紹了許多重構的技巧與名詞,
搭配ReSharperRefactor使用會更有感覺!

下面會有一堆快捷鍵,
真的記不住至少要記住快捷鍵Ctrl+Shift+R - 彈出Refactor的小視窗。

一樣開始之前先來看看我寫的爛Code,
這邊我用了一個完全沒意義的tempVariable來存數字,

List<int> list = new List<int>();
int tempVariable = 10;
list.Add(tempVariable);

Inline

使用快捷鍵Ctrl+Alt+N
可以幫我們將變數內容填到傳入的方法中,

按下確定之後它就幫我們做完了。

在整理程式碼的過程,
如果我覺得我取的變數好像沒什麼意義,
我就會試著Inline回去,
然後再看看這樣是不是比較好理解。

詳細的操作流程可以參考官網提供的範例
我覺得解說還蠻詳細的,
以下僅分享幾個筆者比較熟悉的重構手法:

Introduce

這個手法跟Inline剛好相反,
Inline是把變數回填到方法中,
Introduce則是把方法中值抽取出來。
Introduce可分為以下三種:

  • Introduce Parameter:將值抽取成參數 (快捷鍵Ctrl+Alt+P)
  • Introduce Filed:將值抽取成欄位(快捷鍵Ctrl+Alt+F)
  • Introduce Variable:將值抽取成變數(筆者目前還沒找到快捷鍵,若有大大知道再麻煩分享!)

通常我會在看不懂方法中「傳入值」想表達的意義時使用這個手法,
將其抽取出來,試著搭配命名來解釋「我想幹麻」。

Rename

快捷鍵:Ctrl+R+R

重新命名看似最簡單,
其實是最難的事情。
一個好的命名應該明確的表達意圖
讓別人看你的程式碼就知道「你想幹嘛」,
而不需要任何註解和文件來補充說明。
但要一次命名就到位其實還是需要相當的經驗,

我個人會習慣在進行重構時,
試著以陌生人的角度來看,
只要我覺得不好理解,
或想到更好的命名,
就會把舊的命名給取代掉。

Extract Method

快捷鍵:Ctrl+R+M

方法抽取是最常用的重構手法之一,
我通常會先把比較複雜的程式碼抽出來,
然後在針對那團程式碼逐一拆解,
歸納完主要的邏輯之後再看看是否要調整。
也可能把邏輯主幹歸納完之後再將不必要的變數Inline回去。

結語

就在完賽的前一週,
剛好很巧的去上91哥的「重構與TDD實戰」,
也激起我想用「重構」這個主題做結尾的念頭。

講師在上課的過程中,
透過直接Live Coding的方式,
不斷的顛覆我對重構的定義,

「你的重構不是我的重構」
「你以為重構只是Rename跟Extract Method嗎?」
「一段程式碼至少要被重構三次以上」

距離上完課已經三天了,
這些聲音還在我耳邊圍繞。

重構會隨著每個人的功力而有不同的結果,
也許有些人會認為「後面有時間再來重構」,
但通常就不會有然後了。
重構應該要在當下的過程就進行,
否則後面再回來Code Review時,
還要花一次功去理解這段程式碼的含意。

ReSharper對於一個開發者的助益實在頗大,
但在費用上的確是一筆開銷。
最後我想引用91哥的一段話作為結語:

買來擺著不用,你就會覺得貴。
讓它(ReSharper)可以幫你賺錢,你就會覺得便宜。

參考
https://www.jetbrains.com/resharper/features/code_refactoring.html


上一篇
[鐵人賽Day29] - 工具篇(3) / Azure DevOps
系列文
菜鳥練等區-ASP.Net Core MVC進化之路30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言