<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>終於明白 | tsungyi knows.</title><link>https://tsungyi.li/Blog/</link><description>Recent content on 終於明白 | tsungyi knows.</description><generator>Hugo</generator><language>en-us</language><lastBuildDate>Wed, 01 Mar 2023 03:10:00 +0800</lastBuildDate><atom:link href="https://tsungyi.li/Blog/index.xml" rel="self" type="application/rss+xml"/><item><title>C++ - User-defined literals</title><link>https://tsungyi.li/Blog/posts/cpp-user-defined-literals/</link><pubDate>Wed, 01 Mar 2023 03:10:00 +0800</pubDate><guid>https://tsungyi.li/Blog/posts/cpp-user-defined-literals/</guid><description>&lt;p&gt;C++ 11 新增 User-defined literals 可以經由程式定義的字面常量後綴，讓我們寫出可讀性更加的程式碼。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;簡單的說，我們可以透過有意義的字面常量後綴將常數或是字串轉換自訂的型別來提高可讀性。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id="user-defined-literals"&gt;User-defined literals&lt;/h2&gt;
&lt;p&gt;目前 User-defined literals 支援以下 8 種語法格式：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;code&gt;decimal-literal ud-suffix&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;octal-literal ud-suffix&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;hex-literal ud-suffix&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;binary-literal ud-suffix&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;fractional-constant exponent-part ﻿(optional) ud-suffix&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;digit-sequence exponent-part ud-suffix&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;character-literal ud-suffix&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;string-literal ud-suffix&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;ul&gt;
&lt;li&gt;1-4 支援不同進位的整數格式&lt;/li&gt;
&lt;li&gt;5, 6 支援不同格式的浮點數&lt;/li&gt;
&lt;li&gt;7 支援字元格式&lt;/li&gt;
&lt;li&gt;8 支援字串格式&lt;/li&gt;
&lt;li&gt;使用者定義的 &lt;code&gt;ud-suffix&lt;/code&gt; 需要以底線 &lt;code&gt;_&lt;/code&gt; 開頭，標準函式庫中定義的則不會以底線開頭。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;編譯器在遇到 User-defined literals 的時候會依據 &lt;code&gt;ud-suffix&lt;/code&gt; 前面的文字的型別推導適當的函式進行轉換。&lt;/p&gt;
&lt;p&gt;首先會透過 unqualified name lookup 找到所有名稱符合的函式集合。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;如果是整數格式：
&lt;ul&gt;
&lt;li&gt;如果符合的函式集合有參數型別為 &lt;code&gt;unsigned long long&lt;/code&gt; 時，會選擇此函式並呼叫 &lt;code&gt;operator &amp;quot;&amp;quot;X(nULL)&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;否則，如果有參數型別為 &lt;code&gt;raw literal operators&lt;/code&gt; (&lt;code&gt;const char*&lt;/code&gt;)時，會選擇此函式並呼叫 &lt;code&gt;operator &amp;quot;&amp;quot;X(&amp;quot;n&amp;quot;)&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;否則，如果有參數型別為 &lt;code&gt;numeric literal operator template&lt;/code&gt; (&lt;code&gt;template&amp;lt;char ...&amp;gt; X operator &amp;quot;&amp;quot;X()&lt;/code&gt;) 時會選擇此函式並呼叫 &lt;code&gt;operator &amp;quot;&amp;quot;X&amp;lt;'C1', 'C2', ..., 'CK'&amp;gt;()&lt;/code&gt;。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;如果是浮點數格式：
&lt;ul&gt;
&lt;li&gt;如果符合的函式集合有參數型別為 &lt;code&gt;long double&lt;/code&gt; 時，會選擇此函式並呼叫 &lt;code&gt;operator &amp;quot;&amp;quot;X(fL)&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;否則，如果有參數型別為 &lt;code&gt;raw literal operators&lt;/code&gt; (&lt;code&gt;const char*&lt;/code&gt;)時，會選擇此函式並呼叫 &lt;code&gt;operator &amp;quot;&amp;quot;X(&amp;quot;f&amp;quot;)&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;否則，如果有參數型別為 &lt;code&gt;numeric literal operator template&lt;/code&gt; (&lt;code&gt;template&amp;lt;char ...&amp;gt; X operator &amp;quot;&amp;quot;X()&lt;/code&gt;) 時會選擇此函式並呼叫 &lt;code&gt;operator &amp;quot;&amp;quot;X&amp;lt;'C1', 'C2', ..., 'CK'&amp;gt;()&lt;/code&gt;。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;如果是字串格式：
&lt;ul&gt;
&lt;li&gt;(C++ 20) 如果有符合的 &lt;code&gt;non-type template parameter of class type&lt;/code&gt; (&lt;code&gt;string literal operator template&lt;/code&gt;)時，會選擇此函式並呼叫 &lt;code&gt;operator &amp;quot;&amp;quot;X&amp;lt;str&amp;gt;()&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;否則會選擇此函式並呼叫 &lt;code&gt;operator &amp;quot;&amp;quot;X (str, len)&lt;/code&gt;。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;如果是字元格式：
&lt;ul&gt;
&lt;li&gt;會選擇此函式並呼叫 &lt;code&gt;operator &amp;quot;&amp;quot;X(ch)&lt;/code&gt;。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="literal-operators--literal-operators-template"&gt;Literal operators / Literal operators template&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Literal operators 需要符合以下格式：&lt;/p&gt;</description></item><item><title>Shell Tips</title><link>https://tsungyi.li/Blog/posts/shell-tips/</link><pubDate>Mon, 07 Nov 2022 10:14:38 +0800</pubDate><guid>https://tsungyi.li/Blog/posts/shell-tips/</guid><description>&lt;p&gt;記錄一些最近寫 Shell 用到的語法&lt;/p&gt;
&lt;h1 id="shellcheck"&gt;ShellCheck&lt;/h1&gt;
&lt;p&gt;如果你是使用 Visual Studio Code 強烈建議可以安裝這個套件 &lt;a href="https://marketplace.visualstudio.com/items?itemName=timonwong.shellcheck"&gt;ShellCheck&lt;/a&gt;。
可以幫你檢查 Shell 的錯誤跟提供建議的寫法。&lt;/p&gt;
&lt;h1 id="shebang"&gt;&lt;code&gt;shebang&lt;/code&gt;&lt;/h1&gt;
&lt;p&gt;相關定義可以參考 &lt;a href="https://zh.wikipedia.org/zh-tw/Shebang"&gt;維基百科&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;shebang 必須寫在 shell 的第一行，用來指示這個腳本使用的直譯器。&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code class="language-shell=" data-lang="shell="&gt;#! /bin/bash

echo &amp;#34;SHELL = $SHELL&amp;#34;
echo &amp;#34;MY SHELL = $(ps -o comm -p $$ | awk &amp;#39;NR==2 { print $1}&amp;#39;)&amp;#34;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;雖然可以用環境變數 &lt;code&gt;SHELL&lt;/code&gt; 取得使用的 Shell 不過並不是很正確。所以需要用其他命令取得正確的 Shell。&lt;/p&gt;
&lt;p&gt;在我的電腦上印出的是使用者預設的 shell 而不是 shebang 指定的 shell。使用 ps 才能拿到正確的 shell。&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;SHELL = /bin/zsh
MY SHELL = /bin/bash
&lt;/code&gt;&lt;/pre&gt;&lt;h1 id="常用的內建變數"&gt;常用的內建變數&lt;/h1&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;$?&lt;/code&gt;: 上一個指令或程式執行的回傳值，通常 0 代表執行成功，0 以外的數字表示失敗&lt;/li&gt;
&lt;li&gt;&lt;code&gt;$n&lt;/code&gt;: n 是數字，表示第幾個參數&lt;/li&gt;
&lt;li&gt;&lt;code&gt;$#&lt;/code&gt;: 參數數量&lt;/li&gt;
&lt;li&gt;&lt;code&gt;$$&lt;/code&gt;: shell 執行的 PID&lt;/li&gt;
&lt;li&gt;如果要取得 pipeline 的其中一個階段的結果可以用 &lt;code&gt;${PIPESTATUS[n]}&lt;/code&gt; 其中的 n 是數字&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="redirect"&gt;redirect&lt;/h2&gt;
&lt;p&gt;通常會用 &lt;code&gt;&amp;gt; info.log&lt;/code&gt; 來把輸出寫到檔案，我們也可以用來將輸出寫到不同的目標 &lt;code&gt;2&amp;gt; error.log | tee output.log&lt;/code&gt;。&lt;/p&gt;</description></item><item><title>GitHub Multiple SSH</title><link>https://tsungyi.li/Blog/posts/github-multiple-ssh/</link><pubDate>Tue, 19 Jul 2022 15:16:33 +0800</pubDate><guid>https://tsungyi.li/Blog/posts/github-multiple-ssh/</guid><description>&lt;h1 id="introduction"&gt;Introduction&lt;/h1&gt;
&lt;p&gt;如果有多個 GitHub 帳號 (個人帳號與工作用的帳號) 都要使用 SSH 連線，因為在同一台電腦上無法使用同一個 SSH Key 綁定到不同的帳號，所以會需要做一些設定才可以使用。&lt;/p&gt;
&lt;h1 id="setting"&gt;Setting&lt;/h1&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;首先要先幫兩個帳號都產生一組 SSH Key
可以參考 GitHub 的說明 &lt;a href="https://docs.github.com/en/authentication/connecting-to-github-with-ssh/generating-a-new-ssh-key-and-adding-it-to-the-ssh-agent"&gt;Generating a new SSH key and adding it to the ssh-agent&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;ssh-keygen -t ed25519 -C &amp;quot;personal_mail@personal.com&amp;quot;&lt;/code&gt;
&lt;code&gt;ssh-keygen -t ed25519 -C &amp;quot;work_mail@company.com&amp;quot;&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;並且將兩個帳號都分別註冊到 GitHub 設定頁面
&lt;a href="https://docs.github.com/en/authentication/connecting-to-github-with-ssh/adding-a-new-ssh-key-to-your-github-account"&gt;Adding a new SSH key to your GitHub account&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;記得兩個帳號都要設定，需要分別登入兩個帳號並且將 public key 註冊。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;接下來需要修改你的 ssh 設定檔： 開啟並編輯 &lt;code&gt;~/.ssh/config&lt;/code&gt;
加上以下設定到檔案中，請依照你前面創造的 ssh 來設定你的設定檔。&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;# Personal account
Host github.com
 HostName github.com
 IdentityFile ~/.ssh/personal_private_key
 IdentitiesOnly yes
 
# Work account
Host github-company
 HostName github.com
 IdentityFile ~/.ssh/work_private_key
 IdentitiesOnly yes
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;strong&gt;NOTE&lt;/strong&gt;: 注意第二個 github 需要幫它取別名。 checkout 的時候要改用這個別名。
如果原本的路徑是 &lt;code&gt;git@github.com:your-company/your-repository.git&lt;/code&gt;
clone 的時候要把 github.com 改成你剛剛設定的別名&lt;/p&gt;</description></item><item><title>C++ - 宣告 vs. 定義</title><link>https://tsungyi.li/Blog/posts/cpp-declare-define/</link><pubDate>Mon, 29 Jun 2020 17:23:06 +0800</pubDate><guid>https://tsungyi.li/Blog/posts/cpp-declare-define/</guid><description>&lt;h1 id="declare-宣告"&gt;Declare 宣告&lt;/h1&gt;
&lt;ul&gt;
&lt;li&gt;宣告是指程式中使用到某個類別或是變數之前，先讓編譯器知道該類別的名稱或是變數的類別與名稱。&lt;/li&gt;
&lt;li&gt;必須要在實際使用之前宣告。因此在標頭檔以及原始檔之中需要使用 &lt;code&gt;#include&lt;/code&gt; 這個預處理器命令將用到的類別介紹給編譯器。
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;#include&lt;/code&gt; 這個預處理器命令實際上是將引入的檔案內容取代掉 &lt;code&gt;#include&lt;/code&gt; 所在的位置，因此大量的 &lt;code&gt;#include&lt;/code&gt; 會大幅增加編譯的時間。&lt;/li&gt;
&lt;li&gt;如果只用到指標或是參考時，在標頭檔可以使用前置宣告 (Forward Declare) 的技巧來優化。（細節在 &lt;strong&gt;前置宣告&lt;/strong&gt; 小節中說明）
&lt;ul&gt;
&lt;li&gt;不呼叫該指標的方法或是使用它的成員時，編譯器不需要知道該類別的形狀，只需要當成該類型的指標變數來處理。因為指標的大小是固定的，所以不需要引入該類別也可以通過編譯。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;相同的宣告可以在專案中重複出現。&lt;/li&gt;
&lt;/ul&gt;
&lt;h1 id="define-定義"&gt;Define 定義&lt;/h1&gt;
&lt;ul&gt;
&lt;li&gt;定義是編譯器提供類型實作細節的地方，以及配置變數記憶體的語句。
&lt;ul&gt;
&lt;li&gt;因為定義變數時需要配置記憶體在 stack 上，因此編譯器在此時必須要知道此類別的記憶體佈局（成員的順序與類型、記憶體對齊方式等&amp;hellip;）。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;與宣告不同的是，定義在程式中只能出現一次。重複定義會造成錯誤。
&lt;ul&gt;
&lt;li&gt;同一個轉換單元（Translation Unit）之中只能定義一次，否則會有編譯錯誤。&lt;/li&gt;
&lt;li&gt;同專案中不同轉換單元之中也只能定義一次，否則會有連結錯誤。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;所有程式中有用到的實體（變數、函式&amp;hellip;）都必須也只能在其中一個轉換單元中定義，否則會有連結錯誤。&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="extern-關鍵字"&gt;&lt;code&gt;extern&lt;/code&gt; 關鍵字&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;在多個轉換單元之間，如果要使用同一個變數的實例可以將變數宣告為外部引用（extern），讓編譯器在連結期再把實際的變數實例連結到使用的程式碼。&lt;/li&gt;
&lt;/ul&gt;
&lt;h1 id="forward-declare-前置宣告"&gt;Forward Declare 前置宣告&lt;/h1&gt;
&lt;ul&gt;
&lt;li&gt;需要使用前置宣告通常有兩種原因
&lt;ol&gt;
&lt;li&gt;因為編譯器必須先看到類別、函式的宣告才能在接下來的程式中使用，一但出現互相引用的狀況編譯器就無法處理。例如底下兩個程式碼就無法編譯：&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-cpp" data-lang="cpp"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;void&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;TestFunction&lt;/span&gt;()
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; TestClass test; &lt;span style="color:#75715e"&gt;// error: unknown type name &amp;#39;TestClass&amp;#39;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;class&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;TestClass&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;public&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;void&lt;/span&gt; Test()
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; TestFunction();
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;};
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-cpp" data-lang="cpp"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;class&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;TestClass&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;public&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;void&lt;/span&gt; Test()
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; TestFunction(); &lt;span style="color:#75715e"&gt;// error: use of undeclared identifier &amp;#39;TestFunction&amp;#39;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;};
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;void&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;TestFunction&lt;/span&gt;()
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; TestClass test;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;ul&gt;
&lt;li&gt;不論是先定義 TestFunction 還是先定義 TestClass，都會在使用到對方的程式碼的時候發生編譯錯誤。&lt;/li&gt;
&lt;li&gt;前置宣告的作法就是把其中一個的定義語法改成宣告語法，先讓編譯器知道名稱、型別資訊來避免編譯錯誤。&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-cpp" data-lang="cpp"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;void&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;TestFunction&lt;/span&gt;(); &lt;span style="color:#75715e"&gt;// NOTE: Forward declare
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;class&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;TestClass&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;public&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;void&lt;/span&gt; Test()
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; TestFunction();
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;};
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;void&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;TestFunction&lt;/span&gt;()
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; TestClass test;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;ol start="2"&gt;
&lt;li&gt;更常見的原因是為了提升編譯速度。當某個類別的方法參數、成員變數需要其他型別時，我們就需要在標頭檔之中引用其他標頭檔。因為 #include 是將引用的檔案展開到 #include 的位置，當引入的數量很多的時候會有大量的 IO 發生。通常標頭檔還會被其他檔案引用，造成倍數的成長。
&lt;ul&gt;
&lt;li&gt;如果是方法的參數或是成員變數用到指標或是參考時，編譯器要知道保留多少空間來儲存這個參數。幸運的是指標或是參考的大小都是固定的，因此編譯器並不需要知道整個類別的記憶體佈局。所以使用前置宣告可以減少需要引入的標頭檔數量。&lt;/li&gt;
&lt;li&gt;減少需要引入的標頭檔數量還可以減少因為修改檔案時需要重新編譯的檔案數量。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id="前置宣告的缺點"&gt;前置宣告的缺點&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;根據 &lt;a href="https://google.github.io/styleguide/cppguide.html"&gt;Google C++ Style Guide&lt;/a&gt; 並不建議使用前置宣告。&lt;/li&gt;
&lt;li&gt;使用前置宣告可能造成對 incomplete type 的操作，這在 C++ 標準是未定義行為(Undefine Behaviour)，因此無法保證程式是否能更正確運行。
&lt;ul&gt;
&lt;li&gt;在 #include 該型別的定義之前絕對不要對該型別做操作。
&lt;ul&gt;
&lt;li&gt;大部分的情況部會有問題，而且誤用時也會有編譯錯誤。但是像是 delete 指標、將指標當成參數是可以通過編譯的（會有警告）。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Ref: &lt;a href="https://docs.microsoft.com/zh-tw/cpp/cpp/declarations-and-definitions-cpp?view=vs-2019"&gt;https://docs.microsoft.com/zh-tw/cpp/cpp/declarations-and-definitions-cpp?view=vs-2019&lt;/a&gt;&lt;/p&gt;</description></item><item><title>C++ - 命名規則</title><link>https://tsungyi.li/Blog/posts/cpp-naming/</link><pubDate>Mon, 29 Jun 2020 14:13:50 +0800</pubDate><guid>https://tsungyi.li/Blog/posts/cpp-naming/</guid><description>&lt;h1 id="general-rule"&gt;General Rule&lt;/h1&gt;
&lt;ul&gt;
&lt;li&gt;程式中的所有類別、變數、函式&amp;hellip;的命名請務必使用讓人看到名稱就可以了解他的用途。
&lt;ul&gt;
&lt;li&gt;使用註解來解釋是不得已的作法，請盡量避免。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;名稱的長度沒有限制，但是請以人類容易閱讀、記憶的原則命名，過長或過短的名稱都不適當。
&lt;ul&gt;
&lt;li&gt;避免無法發音的名稱或是外語的羅馬拼音，除非這是所有成員都理解的專有名稱或是 &lt;strong&gt;領域用語&lt;/strong&gt;。&lt;/li&gt;
&lt;li&gt;不要使用自創的縮寫。&lt;/li&gt;
&lt;li&gt;使用縮寫請確保團隊成員都清楚縮寫的意義。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;命名時請考慮使用共同的 &lt;strong&gt;領域用語&lt;/strong&gt;，降低與其他成員溝通的成本。
&lt;ul&gt;
&lt;li&gt;團隊應該建立自己的 &lt;strong&gt;字彙表&lt;/strong&gt; 提供團隊成員查詢專案使用的 &lt;strong&gt;領域用語&lt;/strong&gt;。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h1 id="引用防護include-guard命名"&gt;引用防護（include guard）命名&lt;/h1&gt;
&lt;ul&gt;
&lt;li&gt;使用 ALL_CAPS （全大寫命名法）命名。命名規則使用檔案完整路徑並且加上 &lt;code&gt;__&lt;/code&gt; 前綴。（Ex. &lt;code&gt;__BATTLE_FLElD_H&lt;/code&gt;）
&lt;ul&gt;
&lt;li&gt;建議改用 &lt;code&gt;#pragma once&lt;/code&gt; 取代引用防護，編譯器能對 &lt;code&gt;#pragma once&lt;/code&gt; 做較好的最佳化。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h1 id="全域變數命名"&gt;全域變數命名&lt;/h1&gt;
&lt;ul&gt;
&lt;li&gt;使用 lower camel case （小駝峰式命名法）命名，並且加上 &lt;code&gt;g_&lt;/code&gt; 前綴。（Ex. &lt;code&gt;bool g_battleField&lt;/code&gt;）
&lt;ul&gt;
&lt;li&gt;請避免使用全域變數。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h1 id="區域變數命名"&gt;區域變數命名&lt;/h1&gt;
&lt;ul&gt;
&lt;li&gt;使用 lower camel case （小駝峰式命名法）命名。（Ex. &lt;code&gt;bool battleField&lt;/code&gt;）&lt;/li&gt;
&lt;/ul&gt;
&lt;h1 id="常數命名"&gt;常數命名&lt;/h1&gt;
&lt;ul&gt;
&lt;li&gt;使用 ALL_CAPS （全大寫命名法）命名。（Ex. &lt;code&gt;const bool BATTLE_FLAG&lt;/code&gt;）&lt;/li&gt;
&lt;/ul&gt;
&lt;h1 id="命名空間命名"&gt;命名空間命名&lt;/h1&gt;
&lt;ul&gt;
&lt;li&gt;使用 upper camel case （大駝峰式命名法）命名。（Ex. &lt;code&gt;namespace BattleField&lt;/code&gt;）&lt;/li&gt;
&lt;/ul&gt;
&lt;h1 id="函式命名"&gt;函式命名&lt;/h1&gt;
&lt;ul&gt;
&lt;li&gt;函式使用 lower camel case （小駝峰式命名法）命名。（Ex. &lt;code&gt;void battleFunction()&lt;/code&gt;）&lt;/li&gt;
&lt;li&gt;參數使用 lower camel case （小駝峰式命名法）命名。（Ex. &lt;code&gt;void battleFunction(bool battleFlag)&lt;/code&gt;）&lt;/li&gt;
&lt;/ul&gt;
&lt;h1 id="列舉命名"&gt;列舉命名&lt;/h1&gt;
&lt;ul&gt;
&lt;li&gt;使用 upper camel case （大駝峰式命名法）命名。（Ex. &lt;code&gt;enum BattleField&lt;/code&gt;）&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="列舉值命名"&gt;列舉值命名&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;使用 upper camel case （大駝峰式命名法）命名。（Ex. &lt;code&gt;Empty = 0&lt;/code&gt;）
&lt;ul&gt;
&lt;li&gt;盡量給所有列舉值初始值，避免程式改動後數值錯亂。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;命名時請不要重複列舉的名稱。
&lt;ul&gt;
&lt;li&gt;GOOD: &lt;code&gt;enum BattleField { Empty = 0, Full = 1, };&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;BAD: &lt;code&gt;enum BattleField { BattleFieldEmpty, BattleFieldFull, };&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h1 id="類別命名"&gt;類別命名&lt;/h1&gt;
&lt;ul&gt;
&lt;li&gt;使用 upper camel case （大駝峰式命名法）命名。（Ex. &lt;code&gt;class BattleField&lt;/code&gt;）&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="類別成員命名"&gt;類別成員命名&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;public 成員使用 lower camel case （小駝峰式命名法）命名 （Ex. &lt;code&gt;bool battleFlag&lt;/code&gt;）。
&lt;ul&gt;
&lt;li&gt;請只在 struct 中使用 public 成員。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;protected 、 private 成員使用 lower camel case （小駝峰式命名法）命名，並且加上 &lt;code&gt;_&lt;/code&gt; 前綴。（Ex. &lt;code&gt;bool _battleFlag&lt;/code&gt;）。&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="類別方法類別靜態方法命名"&gt;類別方法/類別靜態方法命名&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;函式使用 lower camel case （小駝峰式命名法）命名 （Ex. &lt;code&gt;void battleFunction()&lt;/code&gt;）&lt;/li&gt;
&lt;li&gt;參數使用 lower camel case （小駝峰式命名法）命名 （Ex. &lt;code&gt;void battleFunction(bool battleFlag)&lt;/code&gt;）&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="類別靜態成員命名"&gt;類別靜態成員命名&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;使用 lower camel case （小駝峰式命名法）命名，並且加上 &lt;code&gt;s_&lt;/code&gt; 前綴。 （Ex. &lt;code&gt;static bool s_battleFlag&lt;/code&gt;）。&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="類別成員常數類別靜態成員常數命名"&gt;類別成員常數/類別靜態成員常數命名&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;使用 ALL_CAPS （全大寫命名法）命名。 （Ex. &lt;code&gt;static const bool BATTLE_FLAG&lt;/code&gt;）。&lt;/li&gt;
&lt;/ul&gt;</description></item><item><title>Unity - Null &amp; Missing Reference Validator</title><link>https://tsungyi.li/Blog/posts/unity-null-and-missing-reference/</link><pubDate>Fri, 06 Dec 2019 03:41:00 +0800</pubDate><guid>https://tsungyi.li/Blog/posts/unity-null-and-missing-reference/</guid><description>&lt;h2 id="notnullattribute"&gt;NotNullAttribute&lt;/h2&gt;
&lt;p&gt;在 Unity 中如果想要在編譯期增加驗證，確保 GameObject 必須要設定好參考，避免沒有設定好參考執行時出錯。我們可以用一個自訂的 &lt;code&gt;Attribute NotNullAttribute&lt;/code&gt; 來標注需要檢查的成員。並且增加一個檢查函式來檢查參考是否為空。&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-csharp" data-lang="csharp"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;[AttributeUsage(AttributeTargets.Field)]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;public&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;class&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;NotNullAttribute&lt;/span&gt; : Attribute
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;ul&gt;
&lt;li&gt;使用的方式只要在 GameObject 的成員加上 [Not Null] 即可&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;注意！&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;這個檢查，目前只適用在 GameObject 及其子類別的成員。至於其他 Reference Type 的成員（也就是 SerializeField 的成員）。因為 Unity 的 Serialization 機制所致， Reference Type 的成員不會是 null。所以 NotNull 的檢查永遠不會生效。&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-csharp" data-lang="csharp"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;public&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;class&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;TestMonoBehaviour&lt;/span&gt; : MonoBehaviour
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt; [NotNull, SerializeField]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;private&lt;/span&gt; GameObject notNullPrivateItem;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt; 
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt; [NotNull, SerializeField]&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;private&lt;/span&gt; GameObject notNullPrivateItem_Null;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt; [SerializeField]&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;private&lt;/span&gt; GameObject privateItem;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt; [SerializeField]&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;private&lt;/span&gt; GameObject privateItem_Null;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt; [NotNull]&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;public&lt;/span&gt; GameObject notNullPublicItem;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt; [NotNull]&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;public&lt;/span&gt; GameObject notNullPublicItem_Null;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;public&lt;/span&gt; GameObject publicItem;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;public&lt;/span&gt; GameObject publicItem_Null;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="postprocessbuild"&gt;PostProcessBuild&lt;/h2&gt;
&lt;p&gt;另外我們需要一個 PostProcessBuild 來進行驗證，其中的 CheckAllEnabledScenes 跟 CheckAllPrefabs 用來從所有的 Scenes 以及 Prefabs 之中檢查所有的 Components。&lt;/p&gt;</description></item><item><title>Unity - 使用 Null Condition Operator &amp; Null Coalescing Operator</title><link>https://tsungyi.li/Blog/posts/unity-null-condition-operator-and-null-coalescing-operator/</link><pubDate>Mon, 16 Sep 2019 20:35:00 +0800</pubDate><guid>https://tsungyi.li/Blog/posts/unity-null-condition-operator-and-null-coalescing-operator/</guid><description>&lt;p&gt;&lt;strong&gt;Null Conditional Operator&lt;/strong&gt; 跟 &lt;strong&gt;Null Coalescing Operator&lt;/strong&gt; 都是 C# 6.0 的新語法，可以讓你寫出更加精簡而且可讀性佳的語法。但是在 Unity 中使用需要特別注意，在某些情況下，執行結果可能跟你想的不一樣。&lt;/p&gt;
&lt;h2 id="null-condition-operator"&gt;Null Condition Operator (&lt;code&gt;?.&lt;/code&gt;)&lt;/h2&gt;
&lt;p&gt;當 &lt;code&gt;?.&lt;/code&gt; 的左側是 &lt;code&gt;null&lt;/code&gt; 就不會執行右側的語法，如此就可以移除掉很多 &lt;code&gt;null&lt;/code&gt; 檢查讓我們的程式更加簡潔好讀。&lt;/p&gt;
&lt;p&gt;先看一下不使用 &lt;strong&gt;Null Conditional Operator&lt;/strong&gt; 的情況：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-csharp" data-lang="csharp"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;var&lt;/span&gt; text = &lt;span style="color:#66d9ef"&gt;string&lt;/span&gt;.Empty;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; (m_controller != &lt;span style="color:#66d9ef"&gt;null&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; (m_controller.m_display != &lt;span style="color:#66d9ef"&gt;null&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; (m_controller.m_display.m_text != &lt;span style="color:#66d9ef"&gt;null&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; text = m_controller.m_display.m_text.text;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;如果使用 &lt;strong&gt;Null Conditional Operator&lt;/strong&gt; 的話，可以改寫成以下的語法：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-csharp" data-lang="csharp"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;var&lt;/span&gt; text = m_controller?.m_display?.m_text?.text;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; (text = &lt;span style="color:#66d9ef"&gt;null&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; text = &lt;span style="color:#66d9ef"&gt;string&lt;/span&gt;.Empty;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;如果其中任何一部份回傳值為 &lt;code&gt;null&lt;/code&gt;，text 最後的結果就是最後結果的預設值 (default)。對於參考型別來說就是 &lt;code&gt;null&lt;/code&gt; 了，值型別就是 &lt;code&gt;0&lt;/code&gt; 或 &lt;code&gt;false&lt;/code&gt;。&lt;/p&gt;</description></item><item><title>Unity - 存取修飾詞 (Access Modifier) 與 Special folders &amp; Assembly definition files</title><link>https://tsungyi.li/Blog/posts/unity-access-modifier/</link><pubDate>Mon, 12 Nov 2018 01:50:00 +0800</pubDate><guid>https://tsungyi.li/Blog/posts/unity-access-modifier/</guid><description>&lt;h2 id="存取修飾詞-access-modifier"&gt;存取修飾詞 Access Modifier&lt;/h2&gt;
&lt;p&gt;C# 的存取修飾詞有以下六種&lt;/p&gt;
&lt;dl&gt;
&lt;dt&gt;&lt;code&gt;public&lt;/code&gt;&lt;/dt&gt;
&lt;dd&gt;所有人都可以存取&lt;/dd&gt;
&lt;dt&gt;&lt;code&gt;protected&lt;/code&gt;&lt;/dt&gt;
&lt;dd&gt;只有所在類別以及他的的衍生類別可以存取&lt;/dd&gt;
&lt;dt&gt;&lt;code&gt;private&lt;/code&gt;&lt;/dt&gt;
&lt;dd&gt;只有所在類別可以存取&lt;/dd&gt;
&lt;dt&gt;&lt;code&gt;internal&lt;/code&gt;&lt;/dt&gt;
&lt;dd&gt;只有在相同 assembly 的類別可以存取&lt;/dd&gt;
&lt;dt&gt;&lt;code&gt;protected internal&lt;/code&gt;&lt;/dt&gt;
&lt;dd&gt;只有在相同 assembly 的類別或是所在類別以及他的的衍生類別可以存取
(也就是 protected OR internal)&lt;/dd&gt;
&lt;dt&gt;&lt;code&gt;private protected&lt;/code&gt; (C# 7.2 新增)&lt;/dt&gt;
&lt;dd&gt;只有在相同 assembly 的類別以及所在類別以及他的的衍生類別可以存取
(也就是 protected AND internal)&lt;/dd&gt;
&lt;/dl&gt;
&lt;p&gt;從這張簡圖就很容易了解不同修飾詞的範圍
&lt;img src="https://tsungyi.li/Blog/images/Unity-Access-Modifier/access-modifier.png" alt="修飾詞的範圍"&gt;&lt;/p&gt;
&lt;p&gt;另外 Type 只能宣告為 &lt;code&gt;public&lt;/code&gt; 或 &lt;code&gt;internal&lt;/code&gt;，Type Member 可以用所有的存取修飾詞宣告。&lt;/p&gt;
&lt;h3 id="default-access-modifier"&gt;Default Access Modifier&lt;/h3&gt;
&lt;p&gt;預設值總是可見度最低的存取修飾詞。Type 為 &lt;code&gt;internal&lt;/code&gt;，Type Member 為 &lt;code&gt;private&lt;/code&gt;。&lt;/p&gt;
&lt;table&gt;
	&lt;thead&gt;
			&lt;tr&gt;
					&lt;th&gt;&lt;/th&gt;
					&lt;th&gt;&lt;strong&gt;預設的存取修飾詞&lt;/strong&gt;&lt;/th&gt;
					&lt;th&gt;&lt;strong&gt;可用的存取修飾詞&lt;/strong&gt;&lt;/th&gt;
			&lt;/tr&gt;
	&lt;/thead&gt;
	&lt;tbody&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;/td&gt;
					&lt;td&gt;&lt;/td&gt;
					&lt;td&gt;&lt;strong&gt;namespace level&lt;/strong&gt;&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;enum&lt;/td&gt;
					&lt;td&gt;internal&lt;/td&gt;
					&lt;td&gt;public, internal&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;interface&lt;/td&gt;
					&lt;td&gt;internal&lt;/td&gt;
					&lt;td&gt;public, internal&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;class&lt;/td&gt;
					&lt;td&gt;internal&lt;/td&gt;
					&lt;td&gt;public, internal&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;struct&lt;/td&gt;
					&lt;td&gt;internal&lt;/td&gt;
					&lt;td&gt;public, internal&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;delegate&lt;/td&gt;
					&lt;td&gt;internal&lt;/td&gt;
					&lt;td&gt;public, internal&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;strong&gt;type level&lt;/strong&gt;&lt;/td&gt;
					&lt;td&gt;&lt;/td&gt;
					&lt;td&gt;&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;enum&lt;/td&gt;
					&lt;td&gt;private&lt;/td&gt;
					&lt;td&gt;public, protected, internal, private, protected internal, private protected&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;interface&lt;/td&gt;
					&lt;td&gt;private&lt;/td&gt;
					&lt;td&gt;public, protected, internal, private, protected internal, private protected&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;class&lt;/td&gt;
					&lt;td&gt;private&lt;/td&gt;
					&lt;td&gt;public, protected, internal, private, protected internal, private protected&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;struct&lt;/td&gt;
					&lt;td&gt;private&lt;/td&gt;
					&lt;td&gt;public, protected, internal, private, protected internal, private protected&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;delegate&lt;/td&gt;
					&lt;td&gt;private&lt;/td&gt;
					&lt;td&gt;public, protected, internal, private, protected internal, private protected&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;enum value&lt;/td&gt;
					&lt;td&gt;public&lt;/td&gt;
					&lt;td&gt;public&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;interface member&lt;/td&gt;
					&lt;td&gt;public&lt;/td&gt;
					&lt;td&gt;public&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;class member&lt;/td&gt;
					&lt;td&gt;private&lt;/td&gt;
					&lt;td&gt;public, protected, internal, private, protected internal, private protected&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;struct member&lt;/td&gt;
					&lt;td&gt;private&lt;/td&gt;
					&lt;td&gt;public, internal, private&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;以軟體設計的原則，應該讓類別、變數的可見性最小化。也就是說不需要公開的類別就不應該設為 &lt;code&gt;public&lt;/code&gt;。&lt;/p&gt;</description></item><item><title>Unity - 忽略警告訊息</title><link>https://tsungyi.li/Blog/posts/unity-ignore-warning/</link><pubDate>Wed, 07 Nov 2018 15:49:00 +0800</pubDate><guid>https://tsungyi.li/Blog/posts/unity-ignore-warning/</guid><description>&lt;p&gt;在 Unity 中如果有用到其他 Plugins 經常會發現許多來自這些 Plugins 的 Warning。如果不處理的話，這些訊息可能會淹沒了專案中真正需要注意的警告訊息。&lt;/p&gt;
&lt;p&gt;底下是來自我們目前專案的警告訊息：&lt;/p&gt;
&lt;p&gt;Unity 看到的警告訊息大約 300個
&lt;img src="https://tsungyi.li/Blog/images/Unity-Ignore-Warning/Warning-1.png" alt="Unity 看到的警告訊息大約 300個"&gt;&lt;/p&gt;
&lt;p&gt;從 Visual Studio for Mac 看到的警告訊息超過 2000個
&lt;img src="https://tsungyi.li/Blog/images/Unity-Ignore-Warning/Warning-2.png" alt="從 Visual Studio for Mac 看到的警告訊息超過 2000個"&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;NOTE&lt;/strong&gt;: Unity 跟 Visual Studio for Mac 的警告訊息數量不同，最好兩個都檢查&lt;/p&gt;
&lt;p&gt;雖然理論上警告訊息都必須要修正，但是專案進行中總是有很多無法掌握的情形，
以下介紹我在目前處理的專案中，遇到難以修改的警告訊息的解決方式：&lt;/p&gt;
&lt;h2 id="plugins-warning"&gt;Plugins&amp;rsquo; warning&lt;/h2&gt;
&lt;p&gt;對於第三方的 Plugins 我們可以先用 &lt;code&gt;#pragma warning&lt;/code&gt; 命令來暫時關閉警告訊息。&lt;/p&gt;
&lt;p&gt;關閉的方式使用 &lt;code&gt;#pragma warning disable&lt;/code&gt; 暫時關閉，並在使用後用 &lt;code&gt;#pragma warning restore&lt;/code&gt; 開啟警告。&lt;/p&gt;
&lt;p&gt;通常會在發生警告前一行加上 &lt;code&gt;#pragma warning disable *warning-list*&lt;/code&gt;，並且在發生警告後一行加上 &lt;code&gt;#pragma warning restore *warning-list*&lt;/code&gt; 重新開啟警告。&lt;/p&gt;
&lt;p&gt;一般來說，建議將關閉警告與開啟警告的範圍越小越好，避免意外隱藏其他有意義的警告訊息。&lt;/p&gt;
&lt;p&gt;如果檔案裡的警告太多，也可以在檔案開頭與結尾分別加上關閉與還原檔案內所有警告訊息。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;NOTE&lt;/strong&gt;: 這樣的關閉方式，一但更新 Plugins 的時候可能要重新處理一次。&lt;/p&gt;</description></item></channel></rss>