從VS 2005提供Code Snippets功能看程式碼複用

weixin_33766168發表於2017-12-18

首先說明這裡的程式碼複用指的是狹義的原始碼的複用,而不是廣義的黑盒複用和白合複用所指的程式碼複用,所以文中所說的程式碼複用都預設都是指前者所指的原始碼的複用。

    我們知道在程式設計中複用程式碼並不是一件容易的事情,其實每段程式碼的編寫,都是通過一定的思考的,當然考慮的程度與其解決的問題的難度有關。我們很忌諱在一個工程中重複使用相同的原始碼,如果出現這樣的情況很多時候就是程式結構設計本身的不合理了。可是對於一個developer或一個product team,原始碼的邏輯(主要是執行邏輯,而不是業務邏輯)在不同專案或同一專案不同版本中很可能需要複用,而這種複用很多時候又是很難抽象成廣義的程式碼複用的。於是我們不得不ctrl+c & ctrl+v,這是一個好辦法,也確實能為我們節省很多的時間,可是這樣同時也會帶來很多的問題。如果被複用的程式碼是近期所寫的,我們一般對其很熟悉,ctrl+v時心理也很有底。可是對於編寫了很久的程式碼,我們在ctrl+v就不得不考慮一下程式碼是不時完全合適被複用了。更多的時候我們還需要對複用程式碼做一些小的修改,最簡單的修改莫過於把變數名改來適合當前程式碼的上下文。

    如果有這樣複用過程式碼的人可能會有一種感覺,我總是不知道哪個原始碼檔案裡的程式碼snippet是我覺得解決這個邏輯最好的@_@,因為每次ctrl+v後或多或少又會把程式碼做一些小的改動,或是修補不足或是增加功能。特別是修補了不足後如果能保留下來以做再次複用就好了,可是很多時候這些越來越優秀的程式碼snippet卻仍然分佈在某個專案的某個檔案中。於是我們發現有些小工具可以用來幫助我們儲存程式碼snippet,比如前些天有人開發的類似CodeLib什麼的。

    今天在用VS.NET 2005的時候,發現tools選單裡有一個Code Snippets Manager(或者用Ctrl+k,Ctrl+b撥出)。開啟看了一下,第一感覺就是一個程式碼模版管理嘛,看看了些預置的snippet,居然都是些很簡單的類似if語句結構、for語句結構、do語句結構、while語句結構等,覺得真是沒有意思,難道微軟要我們用滑鼠來程式設計嗎?繼續往後翻,發現了幾個比較複雜的snippets,有的有一屏那麼多的程式碼。其實程式碼多少不是snippet的重要的問題了,而是這些snippet在插入IDE後,可以根據其在檔案裡的命令定義,產生如下圖所示的自動添補提示功能:
   CodeSnippet.gif 
    在圖中黃底黑字區域內編輯完後按Tab,其下面的虛線框裡的內容會同步的更新。這樣的功能為我們收集並複用原始碼提供了很便利的支援,這個snippet是以xml格式儲存的,只是whidbey beta1裡面還沒沒有提供snippet檔案的視覺化編輯器,不過xml本身在VS.NET裡也不難寫。上圖示例的程式碼檔案如下:

ExpandedBlockStart.gif#region CodeSnippet
None.gif<CodeSnippet Format="1.0.0">
None.gif    <Header>
None.gif        <Title>"named" iterator / indexer pair</Title>
None.gif        <Shortcut>iterindex</Shortcut>
None.gif        <Description>Implement a "named" iterator / indexer pair, using a nested class</Description>
None.gif        <SnippetTypes>
None.gif            <SnippetType>Expansion</SnippetType>
None.gif        </SnippetTypes>
None.gif    </Header>
None.gif    <Snippet>
None.gif        <Declarations>
None.gif            <Literal>
None.gif                <ID>type</ID>
None.gif                <Default>ElementType</Default>
None.gif                <ToolTip>Type to return from iterator / indexer</ToolTip>
None.gif            </Literal>
None.gif            <Literal>
None.gif                <ID>name</ID>
None.gif                <Default>MyView</Default>
None.gif                <ToolTip>Name of the iterator/indexer pair</ToolTip>
None.gif            </Literal>
None.gif            <Literal Editable="false">
None.gif                <ID>outer</ID>
None.gif                <Default>MyOuterClass</Default>
None.gif                <Function>ClassName()</Function>
None.gif            </Literal>
None.gif            <Literal Editable="false">
None.gif                <ID>SystemCollectionsGenericIEnumeratorG</ID>
None.gif                <!-- Function>ShortName(System.IEnumerator)</Function -->
None.gif                <Default>System.Collections.Generic.IEnumerator</Default>
None.gif            </Literal>
None.gif            <Literal Editable="false">
None.gif                <ID>SystemNotImplementedException</ID>
None.gif                <!-- Function>ShortName(System.NotImplementedException)</Function -->
None.gif                <Default>System.NotImplementedException</Default>
None.gif            </Literal>
None.gif        </Declarations>
None.gif        <Code Language="csharp" Format="CData"><![CDATA[public $name$Impl $name$
None.gif        {
None.gif            get
None.gif            {
None.gif                return new $name$Impl(this);
None.gif            }
None.gif        }
None.gif
None.gif        public class $name$Impl
None.gif        {
None.gif            readonly $outer$ _outer;
None.gif
None.gif            internal $name$Impl($outer$ mc)
None.gif            {
None.gif                this._outer = mc;
None.gif            }
None.gif
None.gif            // A Length property isn't required, but it's often useful.
None.gif            public int Length { get { return 1; } }
None.gif
None.gif            public $type$ this[int index]
None.gif            {
None.gif                get
None.gif                {
None.gif                    //
None.gif                    // TODO: implement indexer here
None.gif                    //
None.gif                    // you have full access to $outer$ privates.
None.gif                    //
None.gif                    $end$throw new System.NotImplementedException();
None.gif                    return default($type$);
None.gif                }
None.gif            }
None.gif
None.gif            public $SystemCollectionsGenericIEnumeratorG$<$type$> GetEnumerator()
None.gif            {
None.gif                // TODO: provide an appropriate implementation here
None.gif                for (int i = 0; i < this.Length; i++)
None.gif                {                 
None.gif                    yield return this[i];
None.gif                }
None.gif            }
None.gif        }
]]>
None.gif    </Code>
None.gif    </Snippet>
None.gif</CodeSnippet>
  #endregion

    有了這個snippet的管理支援和方便的使用方法後,基本就解決了我前面提到原始碼複用裡面版本控制和變數名修改的問題。

    其實這個snippet的使用還有一個積極的意義,我們知道編寫相同的邏輯過程是枯燥的,我們都喜歡編寫新的邏輯過程,即使新的邏輯更難更復雜,反而更有挑戰。但是我們又不能避開很多程式邏輯的複用,在我們以一種類似"厭惡"的情緒重複著那些程式邏輯時,保證程式碼的正確性真是一場噩夢emdgust.gif


本文轉自部落格園鳥食軒的部落格,原文連結:http://www.cnblogs.com/birdshome/,如需轉載請自行聯絡原博主。 

相關文章