程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> .NET網頁編程 >> C# >> 關於C# >> [C# 3.0 入門] [第一章 Lambda表達式] 第四節

[C# 3.0 入門] [第一章 Lambda表達式] 第四節

編輯:關於C#

[C# 3.0 入門] [第一章 Lambda表達式] 第四節:Lambda的用途 & 類型聲明能夠和不能夠省略的情況

成問題的是,雖然為了源代碼的簡潔性,很想用Lambda表達式,但是要寫的代碼卻不能全部都用Lambda表達式來寫。

那麼, Lambda表達式究竟能做到什麼程度呢?

習慣了C/C++編程風格的程序員,一定以為因C#語法與之很相似,所以用C#編寫相對復雜的程序應該也沒有問題。可是很遺憾,情況不是這樣。那是因為C/C++具有能寫出復雜功能的表達式的逗號表達式,而C#卻沒有。

例如,C/C++中,下面的代碼是可行的。

int a,b,c;

c = (a=1, b=2, a+b); // a=1、b=2、c=a + b
printf("%d\n",c);

正因為僅僅一個表達式就能做相當多的處理,所有逗號表達式才有著重要的意義。

但是,C#卻沒有這樣的用法。

但是C#有三元運算符?:和空接合運算符??。通過使用這些運算符,相當數量的代碼都可以用Lambda表達式來寫了。

例如,“根據參數指定的文件名向文件中寫入字符串,參數為null的情況下,用‘default.txt’作為默認文件名”這樣的Lambda表達式,像下面那樣,用空接合運算符??就可以用Lambda表達式來寫。

using System;

class Program
{
  static void Main(string[] args)
  {
    Action<string> method =
      (filename) => System.IO File.WriteAllText(
                          filename ?? "default.txt", "Hello!");

    method(null); // 生成default.txt
    method("hello.txt"); // 生成hello.txt
  }
}

List11 使用了空接合運算符??的Lambda表達式

或者“參數的flag如果是false的話,文件名就是‘normal.log’,true的話就是‘system.log’”的情況下,使用三元運算符(?:)按照如下的方式以表達式形式的Lambda來寫。

using System;

class Program
{
  static void Main(string[] args)
  {
    Action<bool> method =
      (system) => System.IO.File.AppendAllText(
          system ? "system.log" : "normal.log", "log message\r\n");

    method(false); // 生成normal.log
    method(true); // 生成system.log
  }
}

List12 使用三元運算符(?:)的Lambda表達式

但是,前一章的List10的例子裡,其中的if語句中就不能改用三元運算符來替換。如果試圖替換的話就是以下情況:

using System;

class Program
{
  static void Main(string[] args)
  {
    Action<string> method = (filename) =>
      filename == null
        ? Console.WriteLine("Hello!")
        : System.IO.File.WriteAllText(filename, "Hello!");

    method(null);
    method("hello.txt");
  }
}

List13 List10中用三元運算符改寫後(產生編譯錯誤)

這個代碼,會產生以下的編譯錯誤:

error CS0201: 只有 assignment、call、increment、decrement 和 new 對象表達式可用作語句。

error CS0173: 無法確定條件表達式的類型,因為“void”和“void”之間沒有隱式轉換。

這並不是說Lambda表達式不能調用具有void返回值的方法。下面的代碼就沒有問題。

Action<string> method =
  (filename) => System.IO.File.WriteAllText(filename, "Hello!");

這裡產生error的原因是,三元運算符的第二個、第三個運算數不能寫成void類型的表達式(因為這樣寫,void沒法隱式轉換成第二個、第三個運算數的類型,所以整個表達式的類型就無法判斷了。

因為存在這樣的問題,有void類型的返回值的表達式情況下,三元運算符使用就很困難。其實,void返回值的表達式很難理解,不能寫反而是個好事。

除了void型以外,其它類型的表達式,使用三元運算符就沒有問題。下面的代碼在編譯和執行時就沒有問題。

1using System;
 2
 3class Program
 4{
 5  static void Main(string[] args)
 6  {
 7    Func<int, bool> method = (year) =>
 8      year < 1994 ? year % 4 == 0 : year % 4 == 2;
 9
10    Console.WriteLine("冬奧會年份");
11    for (int i = 1988; i < 1999; i++)
12    {
13      Console.WriteLine("{0}年={1}", i, method(i));
14    }
15    // 輸出:
16    // 冬奧會年份
17    // 1988年=True
18    // 1989年=False
19    // 1990年=False
20    // 1991年=False
21    // 1992年=True
22    // 1993年=False
23    // 1994年=True
24    // 1995年=False
25    // 1996年=False
26    // 1997年=False
27    // 1998年=True
28  }
29}
30

List14 使用三元運算符的Lambda表達式

代入變量method的Lambda表達式,判斷參數的年份是不是冬奧會舉行的年份,1994年以後,因為是兩年舉行一屆,所以判斷方式也要改變。這種情況下,當然是三元運算符派上用場的時候。總之,這種程度的問題,用表達式形式的Lambda就比較容易寫。

不用類型聲明的情況和必須類型聲明的情況

大部分情況下,Lambda表達式的參數類型都可以省略。然而,Lambda表達式的使用也存在限制,不能推定出類型的情況下就不能用Lambda表達式。

例如,Lambda表達式就不能代入到使用var關鍵字隱式類型聲明的局部變量:

var Lambda = (int x) =>x *2;

上面的代碼會產生“Lambda表達式局部變量隱式的類型聲明不會起作用”的錯誤。避免錯誤的方法是不要用var,而是明確的進行類型聲明。

然而,下面的例子

1using System;
 2
 3delegate int delegate1( int x );
 4delegate int delegate2( string s );
 5
 6class Program
 7{
 8  private static void sample(delegate1 method)
 9  {
10    Console.WriteLine("void Sample(delegate1 method)");
11  }
12
13  private static void sample(delegate2 method)
14  {
15    Console.WriteLine("void Sample(delegate2 method)");
16  }
17
18  static void Main(string[] args)
19  {
20    sample((int x) => 0);
21    // sample((x) => 0); // 如果沒有參數類型聲明會產生錯誤
22  }
23}
24

List 15 必須指定參數類型的情況

這個例子中,“sample((x)=> 0”會產生編譯錯誤。滿足條件的sample方法有兩個,所有就不能確定究竟應該使用哪個。然而,在參數前加上類型聲明“sample((int x) => 0”,就能夠編譯執行。因為參數類型的指定,在2個sample方法中,有一個與之類型相吻合,所以以此為依據就能夠選擇了。

什麼都不做的Lambda表達式

這個話題說到此,還有盲點。這裡先說明一下什麼都不做的Lambda表達式的寫法。

Lambda表達式沒有返回值的情況(void的情況),想使其內容為空的情況下(調用後什麼也不執行的Lambda表達式選擇使用的情況),可以使用內容為空的Lambda語句。

例如,下面這個的Lambda表達式:

(x) => { };

這樣用Lambda表達式重構,解決了“引入了null值對象”的問題。一句話,不應該用null表示什麼也不做的表達式,而是采用調空Lambda表達式的手法。

簡單的說,分別用代碼來展示能夠使用和不能夠使用這個技術的場合。

首先,說說不能夠使用該技術的場合。下面的代碼,因為什麼也不需要處理,所以用null值表示的例子。Sample方法的參數action,僅在值不為null的情況下被調用。

using System;

class Program
{
  private static void Sample(Action<string> action)
  {
    if (action != null) action("Hello!");
  }

  static void Main(string[] args)
  {
    Action<string> action = null;
    Sample(action);

    action = (x) => Console.WriteLine(x);
    Sample(action); // 輸出:Hello!
  }
}

List 16 執行時沒什麼可處理的情況下用null表示的例子

相反,下面的代碼,在沒什麼要執行的情況下,要使用空Lambda表達式表示的情況。沒必要判定Sample方法的參數action是否為null。如果需要處理的內容不存在的情況下,僅僅用空的Lambda表達式來執行,什麼也不做就返回。

using System;

class Program
{
  private static void Sample(Action<string> action)
  {
    action("Hello!");
  }

  static void Main(string[] args)
  {
    Action<string> action = (x) => { };
    Sample(action);

    action = (x) => Console.WriteLine(x);
    Sample(action); // 輸出:Hello!
  }
}

List 17 沒什麼可處理的情況下用空表達式的例子

這樣的“什麼也不做的Lambda表達式(或是以前的匿名方法)”,是筆者經常使用的技術。

例如,現在正在寫的程序,具有用戶的操作用報表的形式回放的功能,通過該功能進行自動測試。這個時候,回放中與輸出有關的處理會全部禁用,以提高其運行效率。這些操作的實現,並不需要具有輸出功能的方法對條件進行一個一個的判斷,只要用“空Lambda表達式(匿名方法)”就行了。因此,源代碼仍然能夠維持其簡潔,成功實現了隨時都能夠執行的自動測試效率的目的。

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved