C# vs Java:C# 五個不可替代的特性瞬間秒殺 Java

oschina發表於2017-08-18

  如果我們可以同時擁有 C# 和 Java 世界的最好特性,那會是什麼樣呢?

  完美的程式語言並不存在,我希望我們可以在這一點上達成一致。開發新語言往往是為了克服另一種語言的弊端,又不可避免的在某些方面上健壯一些,卻在另一些方面上存在不足。

  C# 與 Java 都起源於 C/C++ 語言,他們在物件導向方面有許多相似之處。除了 Java JVM 和 C# .NET CLR 有許多相同結構上的相似性之外,他們各自的開發團隊都有各自的發展方向,他們關注的是各自的語言應該成為什麼樣子。

  我們並不想糾結於某一個語言比另一個語言好,我們只想羅列出 C# 開發者能用到而 Java 中沒有的那些特性而已。

  下面我們開始吧。

 1. LINQ

  LINQ (Language-Integrated Query,語言整合查詢) 於 2007 年引入到 C#,以幫助開發人員從各種資料來源查詢資料。使用它,我們可以在無需考慮正在呼叫的特定資料庫的語法來編寫查詢語句。LINQ provider 所提供的一個元件將查詢轉換為下層資料來源可讀的格式。例如,如果我們需要從 SQL 資料庫查詢資料,LINQ to SQL provider 程式將把 LINQ 查詢轉換成 T-SQL,以便資料庫可以理解它。

  要在 LINQ 中執行查詢操作,首先獲取資料庫,然後建立查詢,最後執行查詢。在 LINQ to Object 查詢中,這可能僅像一樣程式碼一樣簡單,而不是為每個迴圈編寫巢狀的複雜迭代。

  例如,我們來看看這個程式碼,用於在 C# 中從列表中過濾 2 位數。

  首先,在不使用 LINQ 的情況下:

List<int> FilterTwoDigitNumbersWithoutLinq(List<int> numbers)
{
    var tens = new List<int>();
    
    for (var i=0; i < numbers.Count(); i++)
    {
        if ((9 < numbers[i]) && (numbers[i] < 100))
        {
            tens.Add(numbers[i]);
        }
    }
    
    return tens;
}

  如果使用 LINQ 查詢語法形式:

List<int> FilterTwoDigitNumbersWithLinq(List<int> numbers)
{
    return (from a in numbers
            where (a > 9 && a < 100)
            select a).ToList();
}

  或者是方法語法形式:

List<int> FilterNonTwoDigitNumbersWithLinq2(List<int> numbers)
{
    return numbers.Where(a => a > 9 && a < 100).ToList();
}

  這裡兩種語法都是正確的,唯一的區別就是查詢語法看起來更像是 SQL 語句而方法語法使用 lambda 表示式(當然,看起來很像我們在 Java 裡寫的某些程式碼)

  綜述:LINQ 所依賴的許多特性,如 lambda 表示式(就 LINQ 來說非常有用),已經在 Java 中有了等效的實現,儘管我們可以使用流和 lambda 來查詢資料,但 LINQ 簡化了整個過程並且移除了很多在 Java 中存在的冗餘程式碼。

 2. Struct

  C# 中的結構體類似於類。實際上,一個 struct 甚至可以被認為是一個“輕量級類”,因為它可以包含建構函式、常量、方法等等。一個結構體和一個類之間最大的區別在於結構是值型別,而類是引用型別。

  相比於建立類,編寫結構體最重要的好處是在構造一個值型別時比在構造引用型別時更容易確保值語義。如 Microsoft 的文件所述,“struct 型別的變數直接包含結構體的資料,而類型別的變數包含對資料的引用。”因此,對比使用類時,使用結構體的好處之一是,從程式碼的其他部分更改其值的唯一方法是將其作為參考進行顯式傳遞。

  微軟的開發人員建議對於那些小於 16 位元組、生命週期短、不改變的而且不常裝箱的型別,使用結構體(struct)而不是類(class)。在這種情況下,使用結構體可能會比使用類更有效率,因為它會儲存在棧而不是堆中。

  比如:

public struct Point
    {
        public int X;
        public int Y;

        public Point(int X, int Y)
        {
            this.X = X;
            this.Y = Y;
        }

        public static Point operator +(Point p1, Point p2)
        {
            return new Point(p1.X + p2.X, p1.Y + p2.Y);
        }

        public override string ToString()
        {
            return ($"({X}, {Y})");
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            Point point1 = new Point(1, 5);
            Point point2 = new Point(2, 3);

            Console.WriteLine("The addition of both points will result in: {0}", (point1 + point2));

            Console.ReadKey();
        }
}

  小結:很多情況下使用結構體可以節省記憶體分配和釋放的時間,這確實很有吸引力。然而事實是值型別擁有自己的儲存空間。無論結構體擁有如何明顯的優點和缺點,這在 Java 中都不需要操心。

 3. Async/Await

  在一段程式碼中呼叫 async,或者更明確地呼叫方法,這個方法都會在另一個執行緒上執行,不會阻塞當前執行緒。當程式碼執行到 await 命令的時候,它會繼續執行(await 的語句)。如果這時 async 程式碼還沒有完成,那麼執行中的程式會返回到呼叫點。

  這有助於提高應用程式總體的響應速度,以及減少效能瓶頸。在應用程式訪問 Web 和進行所有 UI 相關的活動時,使用非同步程式非常重要。相對於以前的非同步程式設計實現,使用 async/await 可以保留你程式碼的邏輯結構,而編譯器則會擔負起以前由開發者擔負的重擔。

  示例:

class Program
    {
        public static void Main()
        {
            Console.WriteLine("Hey David, How much is 98745 divided by 7?");

            Task<int> david = ThinkAboutIt();

            Console.WriteLine("While he thinks, lets chat about the weather for a bit.");
            Console.WriteLine("Do you think it's going to rain tomorrow?");
            Console.WriteLine("No, I think it should be sunny.");

            david.Wait();
            var davidsAnswer = david.Result;

            Console.WriteLine($"David: {davidsAnswer}");

            Console.ReadKey();
        }

        private static async Task<int> ThinkAboutIt()
        {
            await ReadTheManual();

            Console.WriteLine("Think I got it.");

            return (98745 / 7);
        }

        private static async Task ReadTheManual()
        {
            string file = @"D:\HowToCalc.txt";

            Console.WriteLine("Reading a manual.");
            
            using (StreamReader reader = new StreamReader(file))
            {
                string text = await reader.ReadToEndAsync();
            }

            Console.WriteLine("Done.");
        }
}

  輸出:

// Possible Output:

Hey David, How much is 98745 divided by 7?
Reading a manual.
While he thinks, lets chat about the weather for a bit.
Do you think it's going to rain tomorrow?
No, I think it should be sunny.
Done.
Think I got it.
David: 14106

  概要:CompletableFutures 無疑可以使我們更趨近於擁有等效於 C# 和 Java 所擁有的非同步程式設計中的能力。儘管如此,使用它所帶來的複雜性使其易用度不能與使用 async /await 關鍵字進行的實現相提並論。

 4. Lazy<T> 類

  無論使用 C# 還是 Java,很多人都已經實現了延遲初始化 (或例項化),因此物件要在第一次使用的時候才會被建立。有一種常見的例子是將延遲初始化用於應用程式啟動的時候載入大量物件,但實際需要初始化的物件可能只有少數幾個。這種情況下,我們希望辨別哪些是不需要在這裡初始化的。只初始化那些確實需要初始化的物件可以提升應用程式的效能。

  小結:最近,Lambda 表示式引入到 Java 8 之後,在 Java 中實現延遲載入(還有不少其它事情)變得更容易了。不過,在 C# 中我們可以使用語義化的 Lazy<T> 封裝類來延遲初始化任何類庫或使用者指定的型別。

 5. 一些等價的關鍵詞

  語言中的有用功能不一定像在 C# 中的 LINQ 或 Java 中的模組一樣大。這裡有一些可以幫助 C# 開發人員的關鍵字,它們在 Java 中並沒有:

  a. as

  C# 中的 as 關鍵字會嘗試安全地將物件轉換為某個型別,如果不能轉換的話,就返回 null。與 Java 的instanceof 幾乎等同,但它是一個布林值,如果型別匹配則返回 true,否則返回 false。

  b. Yield

  在 C# 中使用  Yield 和 return yield 來進行自定義且狀態化的迭代,不需要顯式建立額外的類,也不需要建立臨時集合。在 Java 中我們實現迭代最好的選擇是使用外部庫或使用 Java 8 引入的 Lambda 表示式。

  c. var

  Var 是一種隱式型別,其實際型別由編譯器決定,其功能相當於寫一個顯式型別 (比如 int, string 等)。它除了可以減少一些按鍵之外,var 還允許用於匿名型別,而匿名型別在 LINQ 中很常用。我們期待看到“var”標識,備受矚目的 Java SE 9 將實現“將型別推導擴充套件到定義並初始化區域性變數時。”

  d. Checked

  C# 中,我們使用 checked 關鍵字顯式啟用對整型表示式的溢位檢查。如果表示式的運算結果超出目標型別的範圍,我們可以使用 checked 強制要求執行時丟擲 OverflowException。這十分有用,因為常量表示式會在編譯期進行溢位檢查,而非常量表示式不會。

 工具生態系統

  Java 和 C# 之間存在大量的不同之外,當然,其中一些源於 Java 和 .NET 框架的不同。這些不同之處也導致了一些工具在相容性方面的差異,比如 OverOps 在生產監控和錯誤跟蹤方面的差異。

  OverOps 向開發者展示生產中每個錯誤整個呼叫棧的全部原始碼和變數狀態。目前在 .NET 框架上並沒有與之相同的內容,不過在接下來的幾個月內會有一些變化。想了解更多資訊,請點選這裡加入我們 .NET Beta 的等候名單,如果你是 Java 開發者可以去 www.overops.com 檢視演示。

 最後的思考

  在快結束時候,我們這裡提到的大部分功能都在程式碼長度和簡潔程度方面對 C# 開發者有所幫助,這些程式碼不能在 Java 中編寫。事實上這些特性也或多或少說明了 Java 語言冗長的問題,包括最近版本更新帶來的 Lambda 表示式。誠然,很多這些存在於 C# 而不存在於Java 中的特性在常規使用中提供了比使用 Lambda 更簡潔的語法。

  再次說明,我們不想捲入沒完沒了的關於哪種言更好的爭論,我們只是在這裡指出兩種語言之間的一些區別。我們是否遺漏了某些你希望 Java 擁有的特性?請在評論中告訴我們!

  原文地址:http://blog.takipi.com/c-vs-java-5-irreplaceable-c-features-wed-kill-to-have-in-java/

相關文章