2013年5月29日 星期三

[C#]?? Operator (null coalescing operator)

?? operator可以被用在nullable types以及reference types
語法的意思很簡單:如果operand不是null,把他回傳給我;否則給我一個預設的值。比如說以下例子因為x是null所以y被預設成5。
int? x = null;
int y = x ?? 5;  // y is 5
first ?? second語法被執行的步驟如下:

  1. 檢查first 
  2. 如果first不是null,回傳first 
  3. 如果first是null,回傳second

?? operator可以用來串接多個nullable variable以及reference variable。會回傳第一個不是null的變數。以下範例會印出1,因為a是null。
int? a = null, b = 1, c = 2;
Console.WriteLine (a ?? b ?? c);  // 1
善用?? operator可以讓我們的程式碼變得更簡潔,以下是一個範例程式用來計算使用者的年紀,這個例子沒有使用?? operator。
DateTime birth;
DateTime? death;
public TimeSpan Age
{
    get
    {
        if (death == null)
        {
            return DateTime.Now - birth;
        }
        else
        {
            return death.Value - birth;
        }
    }
}
使用?? operator我們可以把上面的範例改寫成好懂又精簡的版本。
DateTime birth;
DateTime? death;
public TimeSpan Age
{
    get
    {
        DateTime lastAlive = death ?? DateTime.Now;
        return lastAlive – birth;
    }
}

[C#]Optional parameters and Named arguments

C#從4.0版之後開始支援optional parameters以及named arguments,這兩個語法通常會一起使用。他讓我們的程式碼變得更簡潔並增加可讀性。首先先來介紹什麼是Parameters and Arguments。

Parameters and Arguments

Parameters和Arguments是兩個很容易搞混的名詞,先來定義這兩者有什麼不同。下面是一段程式碼範例:

void Foo(int x, int y)
{
// Do something with x and y
}
...
int a = 10;
Foo(a, 20);

parameters是function定義時所使用的變數,arguments是我們呼叫function時傳進去的變數或值 範例中的x和y是parameters,a和20是arguments。

Optional parameters

Optional parameters簡單說就是給parameter一個特定的預設值,當使用者沒有傳入此parameter所對應的arguments時,會自動使用預設值。在C#4.0之前沒有optional parameters,如果要達到這個功能必須使用overloading function,會造成寫一大堆overloading function的情況,並不是很好使用。C#4.0之後使用optional parameters可以免去宣告一堆overloading function的困擾。以下是optional parameters的一個簡單範例:

static void Dump(int x, int y = 20, int z = 30)
{
    Console.WriteLine("x={0} y={1} z={2}", x, y, z);
}
...
Dump(1, 2, 3);
Dump(1, 2);
Dump(1);

我們設定y和z的預設值為20和30,當我們在呼叫function時,編譯器會根據我們提供的參數去做對應,找出最合適的function。程式執行結果如下:

x=1 y=2 z=3
x=1 y=2 z=30
x=1 y=20 z=30

程式會從parameters list的左邊到右邊一一去對應參數。如果出現無法對應的情況會丟出exception。

由於optional parameters和arguments的對應是按照宣告的順序,因此當我們呼叫Dump(1, 2)時會自動把y對應成2,沒辦法把2對應到z讓y使用預設值。要讓參數能自由對應需要使用接下來要介紹的named arguments。

parameter的預設值必須是一個常數,因此以下範例是錯誤的,因為DataTime.Now並不是一個常數。

Foo(DateTime dt = DateTime.Now)

Optional parameters也被限制必須放在後面,不能放在非optional parameters之前。

Foo(int x = 0, int y)

Name Arguments

Name arguments讓我們可以不按照順序傳入參數。他的語法是在argument前加入一個對應parameter的名稱。以下是範例:

static void Dump(int x, int y, int z)
{
    Console.WriteLine("x={0} y={1} z={2}", x, y, z);
}
...
Dump(1, 2, 3);
Dump(x: 1, y: 2, z: 3);
Dump(z: 3, y: 2, x: 1);
Dump(1, y: 2, z: 3);
Dump(1, z: 3, y: 2);

不管argument的順序是如何,編譯器會自動幫我們對應到正確的parameters名稱。

x=1 y=2 z=3
x=1 y=2 z=3
x=1 y=2 z=3
x=1 y=2 z=3
x=1 y=2 z=3

Abusing argument evaluation order

int i = 0;
Dump(z: ++i, x: ++i, y: ++i);
x=2 y=3 z=1

上面這行程式碼輸出的結果可能會和你想的不一樣,這是因為傳進去的argument還是根據他宣告的順序做計算,而不是patameters的順序。我們應該要避免寫出這種程式碼。

2013年5月23日 星期四

C# in Depth(精通C#)

這是一本想更深入瞭解C#的人必讀的一本書。作者完整的交代了C#1.0到C#4.0之間的改變,這樣的寫法讓讀這本書有點像在念C#的歷史。書中詳細的說明C#每次改版新增了哪些功能,以及為什麼要新增這些功能,是為了解決什麼樣的問題。

我一直覺得要瞭解一個程式語言的語法最重要的事情就是瞭解語法演進的過程。其實就像學習數學和物理一樣,只會死背公式是沒有用的。程式語言作者設計某種語法一定是有他的原因。如果我們不懂背後設計的原由,就很難活用這個語法。學習程式語言另一個重要事情是此語法實際的用途和例子,比如說使用某種語法可以改善程式的可讀性,讓程式看起來更精簡。這兩點作者都有完整的解釋,這也是這本書為什麼被這麼多人推薦的原因。

書中甚至提到一些compiler相關的東西,像是compiler如何推論匿名型別的正確型別,LINQ和extension methods是如何被compiler轉譯。這些東西在其他C#的書幾乎沒有被解釋。雖然說不用懂這些也能寫程式,不過我相信要寫出好程式多瞭解這些東西是會有幫助的。

這本書雖然有中文版,但是我覺得翻譯不是很理想。在閱讀過程中常常可以看到錯字或缺字,句子翻譯起來不太通順(書中很常出現"然而"有點妨礙閱讀),甚至會有少翻的情況發生。因此還是建議有能力的讀者可以閱讀原文本。另外第二版沒有中文版是蠻可惜的,中文版只有介紹到C#3.0。

總而言之,這是一本C#的經典書籍,很適合學完基本語法後當C#的第二本書,看完之後一定會對C#有更深入的體認

最近看到C# in Depth要出第三版,內容涵蓋了C#5.0的語法。有興趣的人可以到Amazon網站注意出版訊息。

2013年5月20日 星期一

在Visual Studio 2012的C++ Projects中設定target framework

Visual Studio 2012沒有UI可以設定C++ Project的Target Framework
唯一的方法就是去修改.vcxproj裡頭的TargetFrameworkVersion
可以用常用的文字編輯器直接把.vcxproj打開,直接修改TargetFrameworkVersion就可以了
C++ project新建立時候預設會使用v4.0,在.vcxproj裡頭有可能會找不到TargetFrameworkVersion
這時候可以自己加入TargetFrameworkVersion tag到PropertyGroup Globals中
下面是一個範例,此設定檔的Target Framework是v4.5
<PropertyGroup Label="Globals">
      ...
      <RootNamespace>...</RootNamespace>
      <TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
</PropertyGroup>

2013年5月7日 星期二

[Python]如何disassemble Python程式碼

大家都知道Python程式碼會被編譯成Python bytecode並執行在Python的VM上。有沒有什麼方法可以把Python程式碼變成bytecode印出來呢?Python有個disassembler module可以幫助你把Python code轉成bytecode。
以下是一段範例。
>>> import dis
>>> def foo():
...     a = 1
...     b = 2
...     c = a + b
...
>>> dis.dis(foo)
  2           0 LOAD_CONST               1 (1)
              3 STORE_FAST               0 (a)

  3           6 LOAD_CONST               2 (2)
              9 STORE_FAST               1 (b)

  4          12 LOAD_FAST                0 (a)
             15 LOAD_FAST                1 (b)
             18 BINARY_ADD
             19 STORE_FAST               2 (c)
             22 LOAD_CONST               0 (None)
             25 RETURN_VALUE
dis module中的dis method可以幫助你把Python code轉成byte code

2013年5月3日 星期五

[C]#if和#else,切換程式碼的小技巧

在寫程式時當我們修改程式碼時通常會把舊有的程式碼註解起來,再加入新的程式碼。有沒有什麼方法能快速在新舊程式碼之間切換呢?C裡頭的#if和#else是一個好方法。下列是一個常見的新舊程式碼範例,我們用註解把程式碼分開:
                   
newCode();             
/*                     
someOtherCode();        
*/
/*  
newCode();             
*/                      
someOtherCode(); 
其實可以用C裡頭preprocessor的小技巧來快速切換程式碼
#if 1
newCode();  
#else
test(); /* FIXME: please don't to that. */
someOtherCode(); 
#endif    
#if 0
newCode();
#else
test(); /* FIXME: please don't to that. */
someOtherCode(); 
#endif    
如此一來我們只要切換1和0就可以快速切換不同區塊的程式碼,就算裡頭有註解也不用擔心
要註解一大塊程式碼也可以使用這個技巧
#if 0
newCode();
test();
#endid