搜尋
首頁後端開發C#.Net教程教你如何寫出更好的C#程式碼範例

 引言

  開發人員總是喜歡就編碼規範進行爭論,但更重要的是如何能夠在專案中自始至終地遵循編碼規範,以確保專案代碼的一致性。而團隊中的所有人都需要明確編碼規範所扮演的角色。在這篇文章中,我會介紹一些在我多年的從業過程中所學習和總結的一些較好的實踐。

 舉例為先

#   我們先來看一個 FizzBu​​zz 範例。 FizzBu​​zz 要求編寫一個程序,遍歷從 1 到 100 的數字。其中若某數字是 3 的倍數,程式輸出 「Fizz」。如果某數字是 5 的倍數,則輸出 「Buzz」。如果某數字即是 3 的倍數也是 5 的倍數,則輸出 「FizzBu​​zz」。如果數字既不是 3 的倍數也不是 5 的倍數,則只需輸出該數字本身。

#   範例1:

public static void Test()
{
      for (int i = 1; i < 101; i++)
      {
        if (i % 3 == 0 && i % 5 == 0)
        {
          Console.WriteLine("FizzBuzz");
        }
        else if (i % 3 == 0)
        {
          Console.WriteLine("Fizz");
        }
        else if (i % 5 == 0)
        {
          Console.WriteLine("Buzz");
        }
        else
        {
          Console.WriteLine(i);
        }
      }
}

  什麼感覺?這段程式碼需要改進嗎?

  範例2:

public static void Check()
{
      for (int i = 1; i <= 100; i++)
      {
        string output = "";
        if (i % 3 == 0) { output = "Fizz"; }
        if (i % 5 == 0) { output = output + "Buzz"; }
        if (output == "") { output = i.ToString(); }
        Console.WriteLine(output);
      }
}

  現在感覺如何?還能不能進一步改進?

  好,讓我們來試著改進下。程式碼命名對所有軟體開發人員來說都是一件非常困難的事。我們花了大量的時間來做這件事,而且有太多的需要被命名的元素,例如屬性、方法、類別、檔案、項目等。不過我們的確需要花費一些精力在這些命名上,以使程式碼中的名稱更有意義,進而可以提高程式碼的可讀性。

public void DoFizzBuzz()
{
      for (int number = 1; number <= 100; number++)
      {
        var output = GetFizzBuzzOutput(number);
        Console.WriteLine(output);
      }
}

private static string GetFizzBuzzOutput(int number)
{
      string output = string.Empty;
      if (number % 3 == 0)
      {
        output = "Fizz";
      }
      if (number % 5 == 0)
      {
        output += "Buzz";
      }
      if (string.IsNullOrEmpty(output))
      {
        output = number.ToString();
      }
      return output;
}

  這次感覺怎樣?是不是比之前的範例好?是不是可讀性比較好?

 什麼是更好的程式碼?

  首先就是程式碼要為人來寫,其次是為機器。從長期來看,編寫可讀性好的程式碼不會比編寫混亂的程式碼要花更長的時間。如果你能夠非常輕鬆地讀懂你寫的程式碼,那麼想確認其可以正常運作就更容易了。這應該已經是編寫易讀程式碼夠充分的理由了。在很多情況下都需要閱讀程式碼,例如在程式碼審查中會閱讀你寫的程式碼,在你或其他人修復Bug時會閱讀你寫的程式碼,在程式碼需要修改時也會讀到。還有就是當其他人準備在類似的專案或有類似功能的專案中嘗試重複使用你的部分程式碼時也會先閱讀你的程式碼。

  「如果你只為你自己寫程式碼,為什麼要讓程式碼更具可讀性?」

#   好,寫出易讀的程式碼最主要的原因是,在未來的一到兩週,你將工作在另一個專案上。而此時,有其他人需要修復目前專案的一個Bug,那麼會發生什麼事?我敢保證他肯定會迷失在你自己寫的恐怖程式碼中。

#   從我的個人觀點來看,好的程式碼應該要有以下幾個特徵:

  • 程式碼容易編寫,並且易於修改和擴展。

  • # 程式碼乾淨,並表達準確。

  • # 程式碼有價值,並注重品質。

  所以,要時時考慮先為人來寫程式碼,然後再滿足機器的需要。

 如何改進可讀性?

#   首先,你需要閱讀學習其他人寫的程式碼,來了解什麼是好的程式碼,什麼是不好的程式碼。也就是那些你覺得非常容易理解的程式碼,和感覺看起來超級複雜的程式碼。然後,進行實踐。最後花一些時間、經驗和實踐來改進你的程式碼的可讀性。一般來講僅透過培訓這種方式,在任何軟體公司中推動編碼規範都有些困難。而諸如結對程式碼評審,自動化程式碼評審工具等也可以幫助你。目前流行的工具有:

  • FxCop:對 .NET 程式碼進行靜態程式碼分析,提供了多種規則來進行不同形式的分析。

  • # StyleCop:開源項目,其使用程式碼樣式和一致性規範來對分析C#程式碼。可在 Visual Studio 中運行,也可以整合到 MSBuild 中。 StyleCop 也已經被整合到了一些第三方開發工具中。

  • # JetBrains ReSharper:非常著名的提升生產力的工具,可以讓 Microsoft Visual Studio IDE 更加強大。全世界的 .NET 開發人員可能都無法想像,工作上怎麼能沒有 ReSharper 的程式碼審查、程式碼自動重構、快速導航和程式設計助理等這些強大的功能呢。

 规范是什么?

  依据维基百科上的描述:"Coding conventions are a set of guidelines for a specific programming language that recommend programming style, practices and methods for each aspect of a piece program written in this language. These conventions usually cover file organization, indentation, comments, declarations, statements, white space, naming conventions, programming practices, programming principles, programming rules of thumb, architectural best practices, etc. These are guidelines for software structural quality. Software programmers are highly recommended to follow these guidelines to help improve the readability of their source code and make software maintenance easier. Coding conventions are only applicable to the human maintainers and peer reviewers of a software project. Conventions may be formalized in a documented set of rules that an entire team or company follows, or may be as informal as the habitual coding practices of an inpidual. Coding conventions are not enforced by compilers. As a result, not following some or all of the rules has no impact on the executable programs created from the source code."。

  你应该能说出属性、局部变量、方法名、类名等的不同,因为它们使用不同的大小写约定,所以这些约定非常有价值。通过互联网,你已经了解了很多相应的准则和规范,你所需要的仅是找到一种规范或者建立你自己的规范,然后始终遵循该规范。

  下面使用到的源代码(类库设计准则)是由微软的 Special Interest Group 团队开发的,我只是做了些扩展。

  大小写约定

  下面是一些关于C#编码标准、命名约定和最佳实践的示例,可以根据你自己的需要来使用。

  Pascal Casing

  标示符中的首字母,后续串联的每个单词的首字母均为大写。如果需要,标示符的前几个字母均可大写。

  Camel Casing

  标示符的首字母为小写,后续串联的每个单词的首字母为大写。

  参考:标示符大小写规则

 一些命名约定示例

  在互联网上你可以找到足够多的资源,我只是推荐几个其中我最喜欢的:

  • C# 编码约定

  • C# 编码准则

  • C# 编码标准和最佳实践

  • C# 编码规范和命名约定

  这里我展示了一些最基本的示例,但就像我上面已经提到的,找到一个适合你的规范,然后坚持使用。

  要使用 Pascal Casing 为类和方法命名。

public class Product
{
      public void GetActiveProducts()
      {
        //...
      }
      public void CalculateProductAdditinalCost()
      {
        //...
      }
}

  要使用 Camel Casing 为方法的参数和局部变量命名。

public class ProductCategory
{
      public void Save(ProductCategory productCategory)
      {
        // ...
      }
}

  不要使用缩写语。

    // Correct
    ProductCategory productCategory;

    // Avoid
    ProductCategory prodCat;

  不要在标示符中使用下划线。

    // Correct
    ProductCategory productCategory;

    // Avoid
    ProductCategory product_Category;

  要在接口名称前使用字母 I 。

    public interface IAddress
    {
    }

  要在类的顶端定义所有成员变量,在最顶端定义静态变量。

public class Product
{
    public static string BrandName;

    public string Name { get; set; }
    public DateTime DateAvailable { get; set; }

    public Product()
    {
      // ...
    }
}

  要使用单数的词汇定义枚举,除非是BitField枚举。

public enum Direction
{
    North,
    East,
    South,
    West
}

  不要为枚举名称添加Enum后缀。

//Avoid
public enum DirectionEnum
{
    North,
    East,
    South,
    West
}

 为什么我们需要编码规范?

  在大型项目中,开发人员会常依赖于编码规范。他们建立了很多规范和准则,以至于记住这些规范和准则已经变成了日常工作的一部分。计算机并不关心你写的代码可读性是否好,比起读懂那些高级的程序语言语句,计算机更容易理解二进制的机器指令。

  编码规范提供了很多明显的好处,当然有可能你得到的更多。通常这些项目整体范围的规划,将使能够将精力更多的集中在代码中更重要的部分上。

  • 编码规范可以帮助跨项目的传递知识。

  • 编码规范可以帮助你在新的项目上更快速的理解代码。

  • 编码规范强调组织中关联项目间的关系。

  你需要编写可读性高的代码,以此来帮助其他人来理解你的代码。代码命名对我们软件开发人员来说是件非常困难的事情,我们在这上面已经花费了大量的时间,并且有太多的需要命名的元素,例如属性、方法、类、文件、项目等。所以我们确实需要花费一些精力在命名规范上,以使名称更有意义,进而提高代码的可读性。

  还有,编码规范可以让你晚上睡得更香。

 开发人员最应该遵循的几个规则

  始终控制类的大小

  我曾经看到过,并且也曾写过一些超大的类。而且不幸的是,结果总是不好的。后来我找到了真正原因,就是那些超大的类在尝试做太多的事情,这违反了单一职责原则(SRP),也就是面向对象设计原则 SOLID 中的 S。

  “The single responsibility principle states that every object should have a single responsibility, and that responsibility should be entirely encapsulated by the class. All its services should be narrowly aligned with that responsibility.”

  或者按照 Martin Fowler 的定义:"THERE SHOULD NEVER BE MORE THAN ONE REASON FOR A CLASS TO CHANGE."

  为什么一定要将两个职责分离到单独的类中呢?因为每一个职责都是变化的中心。在需求变更时,这个变更将会出现在负责该职责的类中。如果一个类承担了多个职责,就会有一个以上的原因导致其变化。如果一个类有多重职责,则说明这些职责已经耦合到了一起。并且某个职责的变化将有可能削弱或限制这个类满足其他职责的能力。这种耦合将会导致非常脆弱的设计,进而在职责发生变化时,设计可能被意想不到的破坏了。

  避免过时的注释

  先说什么过时的注释。按照 Robert C. Martin 的定义:

  "A comment that has gotten old, irrelevant, and incorrect is obsolete. Comments get old quickly. It is best not to write a comment that will become obsolete. If you find an obsolete comment, it is best to update it or get rid of it as quickly as possible. Obsolete comments tend to migrate away from the code they once described. They become floating islands of irrelevance and misdirection in the code."

  针对这个主题,不同水平的开发人员可能都会有自己的见解。我的建议是尝试避免为单独的方法或短小的类进行注释。因为我所见过的大部分的注释都是在尝试描述代码的目的或意图,或者某些注释可能本身就没什么意义。通常开发人员通过写注释来提高代码的可读性和可维护性,但要保证你所写的注释不会成为代码中的噪音。比起注释,我认为合理的方法命名将更为有效,比如你可以为一个方法起一个更有意义的名字。大部分注释都可能变成了无意义的代码噪音,让我们来看看下面这些注释:

//ensure that we are not exporting
 //deleted products
 if (product.IsDeleted && !product.IsExported)
 {
       ExportProducts = false;
 }

 // This is a for loop that prints the 1 million times
 for (int i = 0; i < 1000000; i++)
 {
       Console.WriteLine(i);
 }

  如果我们不写注释,而是命名一个方法,比如叫 CancelExportForDeletedProducts() ,情况会怎样?所以,合适的方法命名比注释更有效。然而某些情况下,代码注释也会非常有帮助,比如 Visual Studio 会从注释生成 API 文档。此处的注释略有不同,你需要使用 “///” 标识符来注释,这样其他开发人员才能看到 API 或类库的智能提示。

  我没有说总是要避免注释。按照 Kent Beck 说法,可以使用更多的注释来描述程序整体是如何工作的,而不是对单独的方法进行注释。如果注释是在尝试描述代码的目的或意图,那就错了。如果你在代码中看到了密密麻麻的的注释,你可能就会意识到有这么多注释说明代码写的很糟糕。了解更多信息可以阅读下面这几本书:

  • 《Professional Refactoring in C# and ASP.NET》 by Danijel Arsenovski

  • 《重构:改善既有代码设计》 by Martin Fowler, Kent Beck, John Brant, William Opdyke, Don Roberts

  避免不必要的Region

  Region 是 Visual Studio 提供的一个功能,它允许你将代码分块。Region 的存在是因为它可以使大文件导航变得容易。Region 还常被用于隐藏丑陋的代码,或者类已经膨胀的非常大了需要分块。而如果一个类做了太多的事情,也就说明其违反了单一职责原则。所以,下次当你想新增一个 Region 时,先考虑下有没有可能将这个 Region 分离到一个单独的类中。

  保持方法的短小

  方法中的代码行数越多,则方法越难理解。我们推荐每个方法中只包含 20-25 行代码。但有些人说 1-10 行更合理,这只是些个人喜好,没有硬性的规则。抽取方法是最常见的重构方式之一。如果你发现一个方法过长,或者已经需要一个注释来描述它的目的了,那么你就可以应用抽取方法了。人们总是会问一个方法到底多长合适,但其实长度并不是问题的根源。当你在处理复杂的方法时,跟踪所有局部变量是最复杂和消耗时间的,而通过抽取一个方法可以节省一些时间。可以使用 Visual Studio 来抽取方法,它会帮助你跟踪局部变量,并将其传递给新的方法或者接收方法的返回值。

  Using ReSharper

  Using Microsoft Visual Studio

  更多的信息可以参考 MSDN。

  按照《重构:改善既有代码设计》中的描述,

  "Extract Method is one of the most common refactoring I do. I look at a method that is too long or look at code that needs a comment to understand its purpose. I then turn that fragment of code into its own method. I prefer short, well-named methods for several reasons. First, it increases the chances that other methods can use a method when the method is finely grained. Second, it allows the higher-level methods to read more like a series of comments. Overriding also is easier when the methods are finely grained. It does take a little getting used to if you are used to seeing larger methods. And small methods really work only when you have good names, so you need to pay attention to naming. People sometimes ask me what length I look for in a method. To me length is not the issue. The key is the semantic distance between the method name and the method body. If extracting improves clarity, do it, even if the name is longer than the code you have extracted."

  避免过多的参数

  通过声明一个类来代替多个参数。创建一个类,用于包含所有的参数。通常来讲,这是一个较好的设计,并且这个抽象非常的有价值。

//avoid
    public void Checkout(string shippingName, string shippingCity,
      string shippingSate, string shippingZip, string billingName,
      string billingCity, string billingSate, string billingZip)
    {
    }
    //DO
    public void Checkout(ShippingAddress shippingAddress, BillingAddress billingAddress)
    {
    }

  我们需要引入类来代替所有的参数。

  避免复杂的表达式

if(product.Price>500 && !product.IsDeleted && 
  !product.IsFeatured && product.IsExported)
{
      // do something
}

  复杂的表达式意味着其背后隐藏了一些涵义,我们可以通过使用属性来封装这些表达式,进而使代码更易读些。

  把警告等同于错误

  如果你注意看代码,你会发现一个变量被声明了但从没被使用过。正常来讲,我们编译工程后会得到一个警告,但仍可以运行工程而不会发生任何错误。但是我们应该尽可能地移除这些警告。通过如下步骤可以在工程上设置将警告等同于错误:

  精简多处返回

  在每段程序中都减少函数返回的数量。假设从底部开始阅读代码,你很难意识到有可能在上面的某处已经返回了,这样的代码将是非常难理解的。

  仅使用一处返回可以增强可读性。如果程序这么写的话可能看起来比较干净,但不立即返回也意味着需要编写更多代码。

//avoid
      if(product.Price>15)
      {
         return false;
      }
      else if(product.IsDeleted)
      {
         return false;
      }
      else if(!product.IsFeatured)
      {
         return false;
      }
      else if()
      {
         //.....
      }
      return true;
//DO
      var isValid = true;
      if(product.Price>15)
      {
         isValid= false;
      }
      else if(product.IsDeleted)
      {
         isValid= false;
      }
      else if(!product.IsFeatured)
      {
         isValid= false;
      }
      return isValid;

  你可以想象在这 20-30 行代码中就散落了 4 个退出点,这会使你非常难理解到底程序内部做了什么,到底会执行什么,什么时候执行。

  关于这一点我得到了很多人的回复,一些人同意这个观点,有些则不同意这是一个好的编码标准。为了找出潜在的问题,我做了些单元测试,发现如果复杂的方法包含多个退出点,通常情况下会需要一组测试来覆盖所有的路径。

if( BADFunction() == true)
      {
          // expression
          if( anotherFunction() == true )
          {
           // expression
           return true;
          }
          else
          {
               //error
          }
      }
      else
      {
          //error
      }
      return false;
if( !GoodFunction())
      {
          // error.
          return false
      } 
      // expression
      if( !GoodFunction2())
      {
          //error.
          return false;
      }
      // more expression
      return true;

  进一步理解可以参考 Steve McConnell 的《代码大全》。

  使用断言

  在软件开发中,断言代码常被用于检查程序代码是否按照其设计在执行。通常 True 代表所有操作按照预期的完成,False 代表已经侦测到了一些意外的错误。断言通常会接收两个参数,一个布尔型的表达式用于一个描述假设为真的假定,一个消息参数用于描述断言失败的原因。

  尤其在开发大型的、复杂的高可靠系统中,断言通常是非常有用的功能。

  例如:如果系统假设将最多支持 100,000 用户记录,系统中可能会包含一个断言来检查用户记录数小于等于 100,000,在这种范围下,断言不会起作用。但如果用户记录数量超过了 100,000,则断言将会抛出一个错误来告诉你记录数已经超出了范围。

  检查循环端点值

  一个循环通常会涉及三种条件值:第一个值、中间的某值和最后一个值。但如果你有任何其他的特定条件,也需要进行检测。如果循环中包含了复杂的计算,请不要使用计算器,要手工检查计算结果。

 总结

  通常在任何软件公司中推行编码规范都需要按照组织行为、项目属性和领域来进行,在此我想再次强调“找到一个适合你的编码规范,并一直遵循它”。

  如果你认为我遗漏了某个特别有用的编码准则,请在评论中描述,我会尝试补充到文章中。

  Coding For Fun.

 

以上是教你如何寫出更好的C#程式碼範例的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn
char數組在C語言中如何使用char數組在C語言中如何使用Apr 03, 2025 pm 03:24 PM

char 數組在 C 語言中存儲字符序列,聲明為 char array_name[size]。訪問元素通過下標運算符,元素以空終止符 '\0' 結尾,用於表示字符串終點。 C 語言提供多種字符串操作函數,如 strlen()、strcpy()、strcat() 和 strcmp()。

char在C語言字符串中的作用是什麼char在C語言字符串中的作用是什麼Apr 03, 2025 pm 03:15 PM

在 C 語言中,char 類型在字符串中用於:1. 存儲單個字符;2. 使用數組表示字符串並以 null 終止符結束;3. 通過字符串操作函數進行操作;4. 從鍵盤讀取或輸出字符串。

char在C語言中如何處理特殊字符char在C語言中如何處理特殊字符Apr 03, 2025 pm 03:18 PM

C語言中通過轉義序列處理特殊字符,如:\n表示換行符。 \t表示製表符。使用轉義序列或字符常量表示特殊字符,如char c = '\n'。注意,反斜杠需要轉義兩次。不同平台和編譯器可能有不同的轉義序列,請查閱文檔。

c#多線程和異步的區別c#多線程和異步的區別Apr 03, 2025 pm 02:57 PM

多線程和異步的區別在於,多線程同時執行多個線程,而異步在不阻塞當前線程的情況下執行操作。多線程用於計算密集型任務,而異步用於用戶交互操作。多線程的優勢是提高計算性能,異步的優勢是不阻塞 UI 線程。選擇多線程還是異步取決於任務性質:計算密集型任務使用多線程,與外部資源交互且需要保持 UI 響應的任務使用異步。

C語言各種符號的使用方法C語言各種符號的使用方法Apr 03, 2025 pm 04:48 PM

C 語言中符號的使用方法涵蓋算術、賦值、條件、邏輯、位運算符等。算術運算符用於基本數學運算,賦值運算符用於賦值和加減乘除賦值,條件運算符用於根據條件執行不同操作,邏輯運算符用於邏輯操作,位運算符用於位級操作,特殊常量用於表示空指針、文件結束標記和非數字值。

char在C語言中如何進行類型轉換char在C語言中如何進行類型轉換Apr 03, 2025 pm 03:21 PM

在 C 語言中,char 類型轉換可以通過:強制類型轉換:使用強制類型轉換符將一種類型的數據直接轉換為另一種類型。自動類型轉換:當一種類型的數據可以容納另一種類型的值時,編譯器自動進行轉換。

C語言 sum 的作用是什麼?C語言 sum 的作用是什麼?Apr 03, 2025 pm 02:21 PM

C語言中沒有內置求和函數,需自行編寫。可通過遍歷數組並累加元素實現求和:循環版本:使用for循環和數組長度計算求和。指針版本:使用指針指向數組元素,通過自增指針遍歷高效求和。動態分配數組版本:動態分配數組並自行管理內存,確保釋放已分配內存以防止內存洩漏。

char與wchar_t在C語言中的區別char與wchar_t在C語言中的區別Apr 03, 2025 pm 03:09 PM

在 C 語言中,char 和 wchar_t 的主要區別在於字符編碼:char 使用 ASCII 或擴展 ASCII,wchar_t 使用 Unicode;char 佔用 1-2 個字節,wchar_t 佔用 2-4 個字節;char 適用於英語文本,wchar_t 適用於多語言文本;char 廣泛支持,wchar_t 依賴於編譯器和操作系統是否支持 Unicode;char 的字符範圍受限,wchar_t 的字符範圍更大,並使用專門的函數進行算術運算。

See all articles

熱AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover

AI Clothes Remover

用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool

Undress AI Tool

免費脫衣圖片

Clothoff.io

Clothoff.io

AI脫衣器

AI Hentai Generator

AI Hentai Generator

免費產生 AI 無盡。

熱門文章

R.E.P.O.能量晶體解釋及其做什麼(黃色晶體)
3 週前By尊渡假赌尊渡假赌尊渡假赌
R.E.P.O.最佳圖形設置
3 週前By尊渡假赌尊渡假赌尊渡假赌
R.E.P.O.如果您聽不到任何人,如何修復音頻
3 週前By尊渡假赌尊渡假赌尊渡假赌

熱工具

MinGW - Minimalist GNU for Windows

MinGW - Minimalist GNU for Windows

這個專案正在遷移到osdn.net/projects/mingw的過程中,你可以繼續在那裡關注我們。 MinGW:GNU編譯器集合(GCC)的本機Windows移植版本,可自由分發的導入函式庫和用於建置本機Windows應用程式的頭檔;包括對MSVC執行時間的擴展,以支援C99功能。 MinGW的所有軟體都可以在64位元Windows平台上運作。

Safe Exam Browser

Safe Exam Browser

Safe Exam Browser是一個安全的瀏覽器環境,安全地進行線上考試。該軟體將任何電腦變成一個安全的工作站。它控制對任何實用工具的訪問,並防止學生使用未經授權的資源。

SAP NetWeaver Server Adapter for Eclipse

SAP NetWeaver Server Adapter for Eclipse

將Eclipse與SAP NetWeaver應用伺服器整合。

SublimeText3 英文版

SublimeText3 英文版

推薦:為Win版本,支援程式碼提示!

mPDF

mPDF

mPDF是一個PHP庫,可以從UTF-8編碼的HTML產生PDF檔案。原作者Ian Back編寫mPDF以從他的網站上「即時」輸出PDF文件,並處理不同的語言。與原始腳本如HTML2FPDF相比,它的速度較慢,並且在使用Unicode字體時產生的檔案較大,但支援CSS樣式等,並進行了大量增強。支援幾乎所有語言,包括RTL(阿拉伯語和希伯來語)和CJK(中日韓)。支援嵌套的區塊級元素(如P、DIV),