.Net Core 編碼規範

.Net Core 編碼規範

標籤: 未分類程序員

概述

規範制定原則

  • 方便代碼的交流和維護。
  • 不影響編碼的效率,不與大衆習慣衝突。
  • 使代碼更美觀、閱讀更方便。
  • 使代碼的邏輯更清晰、更易於理解。

術語定義

Pascal 大小寫算法

將標識符的首字母和後面鏈接的每一個單詞的首字母都大寫。能夠對三字符或更多字符的標識符使用Pascal 大小寫。例:編程

BackColor框架

Camel 大小寫編輯器

標識符的首字母小寫,而每一個後面鏈接的單詞的首字母都大寫。例:ide

backColor工具

匈牙利命名法oop

匈牙利命名法是一名匈牙利程序員發明的,並且他在微軟工做了多年。此命名法就是經過微軟的各類產品和文檔傳出來的。多數有經驗的程序員,無論他們用的是哪門兒語言,都或多或少在使用它。性能

這種命名法的基本原則是:開發工具

變量名=屬性+類型+對象描述

即一個變量名是由三部分信息組成,這樣,程序員很容易理解變量的類型、用途,並且便於記憶。

下邊是一些推薦使用的規則例子,你能夠挑選使用,也能夠根據我的喜愛做些修改再用之。

  • 屬性部分:
  • 全局變量: g_
  • 常量 : c_
  • 類成員變量: m_

  • 類型部分:
  • 指針: p
  • 句柄: h
  • 布爾型: b
  • 浮點型: f
  • 無符號: u

  • 描述部分:
  • 初始化: Init
  • 臨時變量: Tmp
  • 目的對象: Dst
  • 源對象: Src
  • 窗口: Wnd

下邊舉例說明:

  • hwnd: h表示句柄,wnd表示窗口,合起來爲「窗口句柄」。
  • m_bFlag: m表示成員變量,b表示布爾,合起來爲:「某個類的成員變量,布爾型,是一個狀態標誌」。

代碼外觀

列寬

代碼列寬控制在120字符左右。

換行

當表達式超出或即將超出規定的列寬,遵循如下規則進行換行

  • 在逗號後換行。
  • 在操做符前換行。
  • 規則1優先於規則2。

當以上規則會致使代碼混亂的時候本身採起更靈活的換行規則。

縮進

縮進應該是每行一個Tab(4個空格),不要在代碼中使用Tab字符。

Visual Studio 設置:工具->選項->文本編輯器->C#->製表符->插入空格,製表符大小=4,縮進大小=4

空行

空行是爲了將邏輯上相關聯的代碼分塊,以便提升代碼的可閱讀性。

在如下狀況下使用兩個空行

  • 接口和類的定義之間。
  • 枚舉和類的定義之間。
  • 類與類的定義之間。

在如下狀況下使用一個空行

  • 方法與方法、屬性與屬性之間。
  • 方法中變量聲明與語句之間。
  • 方法與方法之間。
  • 方法中不一樣的邏輯塊之間。
  • 方法中的返回語句與其餘的語句之間。
  • 屬性與方法、屬性與字段、方法與字段之間。
  • 註釋與它註釋的語句間不空行,但與其餘的語句間空一行。

空格

在如下狀況中要使用到空格

  • 關鍵字和左括符 「(」 應該用空格隔開。如:

while (true)

注意在方法名和左括符 「(」 之間不要使用空格,這樣有助於辨認代碼中的方法調用與關鍵字。

  • 多個參數用逗號隔開,每一個逗號後都應加一個空格。
  • 除了 . 以外,全部的二元操做符都應用空格與它們的操做數隔開。一元操做符、++及--與操做數間不須要空格。如:
a += c + d;
a = (a + b)/(c*d);

while (d++ == s++)
{
    n++;
}

PrintSize("size is " + size + "\n");
  • 語句中的表達式之間用空格隔開。如:

for (expr1; expr2; expr3)

括號 - ()

  • 左括號「(」沒關係靠關鍵字,中間用一個空格隔開。
  • 左括號「(」與方法名之間不要添加任何空格。
  • 沒有必要的話不要在返回語句中使用()。

花括號 - {}

  • 左花括號 「{」 放於關鍵字或方法名的下一行並與之對齊。如:
if (condition)
{
}

public int Add(int x, int y)
{
}
  • 左花括號 「{」 要與相應的右花括號 「}」對齊。
  • 一般狀況下左花括號 「{」單獨成行,不與任何語句並列一行。
  • if、while、do語句後必定要使用{},即便{}號中爲空或只有一條語句。如:
if (somevalue == 1)
{
    somevalue = 2;
}
  • 右花括號 「}」 後建議加一個註釋以便於方便的找到與之相應的 {。如:
while(1)
{
    if(valid)
    {
    } // if valid
    else
    {
    } // not valid
} // end forever

程序註釋

註釋概述

  • 修改代碼時,老是使代碼周圍的註釋保持最新。
  • 在每一個例程的開始,提供標準的註釋樣本以指示例程的用途、假設和限制頗有幫助。註釋樣本應該是解釋它爲何存在和能夠作什麼的簡短介紹.
  • 避免在代碼行的末尾添加註釋;行尾註釋使代碼更難閱讀。不過在批註變量聲明時,行尾註釋是合適的;在這種狀況下,將全部行尾註釋在公共製表位處對齊。
  • 避免雜亂的註釋,如一整行星號。而是應該使用空白將註釋同代碼分開。
  • 避免在塊註釋的周圍加上印刷框。這樣看起來可能很漂亮,可是難於維護。
  • 在部署發佈以前,移除全部臨時或無關的註釋,以免在往後的維護工做中產生混亂。
  • 若是須要用註釋來解釋複雜的代碼節,請檢查此代碼以肯定是否應該重寫它。盡一切可能不註釋難以理解的代碼,而應該重寫它。儘管通常不該該爲了使代碼更簡單以便於人們使用而犧牲性能,但必須保持性能和可維護性之間的平衡。
  • 在編寫代碼時就註釋,由於之後極可能沒有時間這樣作。另外,若是有機會複查已編寫的代碼,在今天看來很明顯的東西六週之後或許就不明顯了。
  • 避免多餘的或不適當的註釋,不該包含我的情緒內容,如幽默的不主要的備註。
  • 在編寫註釋時使用完整的句子。註釋應該闡明代碼,而不該該增長多義性。
  • 使用註釋來解釋代碼的意圖。它們不該做爲代碼的聯機翻譯。
  • 註釋代碼中不十分明顯的內容。
  • 爲了防止問題反覆出現,對錯誤修復和解決方法代碼老是使用註釋。
  • 對由循環和邏輯分支組成的代碼使用註釋。這些是幫助源代碼讀者的主要方面。
  • 在整個應用程序中,使用具備一致的標點和結構的統同樣式來構造註釋。
  • 用空白將註釋同註釋分隔符分開。在沒有顏色提示的狀況下查看註釋時,這樣作會使註釋很明顯且容易被找到。
  • 代碼修改變動記錄不該使用註釋標明修改日期和修改人,註釋應只針對代碼不記錄版本,代碼版本應該使用代碼版本系統進行管理
  • 爲了使層次清晰,在閉合的右花括號後註釋該閉合所對應的起點。
namespace SCB.Framework.Web 
{
} // namespace SCB.Framework.Web

文檔型註釋

該類註釋採用.Net已定義好的Xml標籤來標記,在聲明接口、類、方法、屬性、字段都應該使用該類註釋,以便代碼完成後直接生成代碼文檔,讓別人更好的瞭解代碼的實現和接口。如

/// <summary> MyMethod is a method in the MyClass class.
/// <para> Here's how you could make a second paragraph in a description.
/// <see cref="System.Console.WriteLine"/> 
/// for information about output statements.
/// </para>
/// <seealso cref="MyClass.Main"/>
/// </summary>
public static void MyMethod(int Int1)
{
}

註釋標籤的使用請參考:http://msdn.microsoft.com/zh-cn/library/5ast78ax.aspx

類c註釋

該類註釋用於

  • 複雜程序邏輯說明與技術事項

用法

/*
動態路由算法使用Round-robin算法,原理是...
*/

單行註釋

該類註釋用於

  • 方法內的代碼註釋。如變量的聲明、代碼或代碼段的解釋。例:
//
// 註釋語句
// 
private int number;

或

// 註釋語句
private int number;
  • 方法內變量的聲明或花括號後的註釋, 例:
if (1 == 1) // always true
{
    statement; 
} // always true

聲明

每行聲明數

一行只建議做一個聲明,並按字母順序排列。如:

int level; // 推薦
int size; // 推薦
int x, y; // 不推薦

初始化

建議在變量聲明時就對其作初始化。

位置

變量建議置於塊的開始處,不要老是在第一次使用它們的地方作聲明。如:

void MyMethod()
{
    int int1 = 0;

    if (condition)
    {
        int int2 = 0;
        ...
    }
}

例外狀況

for (int i = 0; i < maxLoops; i++)
{
    ...
}

應避免不一樣層次間的變量重名,如:

int count;
...

void MyMethod()
{
    if (condition)
    {
        int count = 0; // 避免
        ...
    }
    ...
}

類和接口的聲明

  • 在方法名與其後的左括號間沒有任何空格。
  • 左花括號 「{」 出如今聲明的下行並與之對齊,單獨成行。
  • 方法間用一個空行隔開。

字段的聲明

不要使用是 public 或 protected 的實例字段。若是避免將字段直接公開給開發人員,能夠更輕鬆地對類進行版本控制,緣由是在維護二進制兼容性時字段不能被更改成屬性。考慮爲字段提供 get 和set 屬性訪問器,而不是使它們成爲公共的。 get 和 set 屬性訪問器中可執行代碼的存在使得能夠進行後續改進,如在使用屬性或者獲得屬性更改通知時根據須要建立對象。下面的代碼示例闡釋帶有 get 和 set 屬性訪問器的私有實例字段的正確使用。例:

public class Control: Component
{
    private int handle;

    public int Handle
    {
        get
        {
            return handle; 
        }
    }
}

命名規範

命名概述

名稱應該說明「什麼」而不是「如何」。經過避免使用公開基礎實現(它們會發生改變)的名稱,能夠保留簡化複雜性的抽象層。例如,可使用 GetNextStudent(),而不是 GetNextArrayElement()。

命名原則是:

選擇正確名稱時的困難可能代表須要進一步分析或定義項的目的。使名稱足夠長以便有必定的意義,而且足夠短以免冗長。惟一名稱在編程上僅用於將各項區分開。表現力強的名稱是爲了幫助人們閱讀;所以,提供人們能夠理解的名稱是有意義的。不過,請確保選擇的名稱符合適用語言的規則和標準。

如下幾點是推薦的命名方法:

  • 避免容易被主觀解釋的難懂的名稱,如方面名 AnalyzeThis(),或者屬性名 xxK8。這樣的名稱會致使多義性。
  • 在類屬性的名稱中包含類名是多餘的,如 Book.BookTitle。而是應該使用 Book.Title
  • 只要合適,在變量名的末尾或開頭加計算限定符(Avg、Sum、Min、Max、Index)
  • 在變量名中使用互補對,如 min/max、begin/endopen/close
  • 布爾變量名應該包含 Is,這意味着 Yes/NoTrue/False 值,如 fileIsFound
  • 在命名狀態變量時,避免使用諸如 Flag 的術語。狀態變量不一樣於布爾變量的地方是它能夠具備兩個以上的可能值。不是使用 documentFlag,而是使用更具描述性的名稱,如 documentFormatType
  • 即便對於可能僅出如今幾個代碼行中的生存期很短的變量,仍然使用有意義的名稱。僅對於短循環索引使用單字母變量名,如 i 或 j。 可能的狀況下,儘可能不要使用原義數字(幻數)或原義字符串,如
    for (int i = 1; i < 7; i++)。而是使用命名常數,如 for (int i = 1; i < NUM_DAYS_IN_WEEK; i++) 以便於維護和理解。

大小寫規則

大寫

  • 組織名縮寫使用大寫
  • 兩個或者更少字母組成的標識符使用大寫。例:
System.IO
System.Web.UI
SCB.Framework.UI

下表彙總了大寫規則,並提供了不一樣類型的標識符的示例。

標識符 大小寫 示例
Pascal AppDomain
枚舉類型 Pascal ErrorLevel
枚舉值 Pascal FatalError
事件 Pascal ValueChange
異常類 Pascal WebException
注意: 老是以 Exception 後綴結尾。
只讀的靜態字段 Pascal RedValue
接口 Pascal IDisposable
注意: 老是以 I 前綴開始。
方法 Pascal ToString
命名空間 Pascal System.Drawing
屬性 Pascal BackColor
公共實例字段 Pascal RedValue
注意: 應優先使用屬性。
受保護的實例字段 Camel redValue
注意: 應優先使用屬性。
私有的實例字段 Camel redValue
參數 Camel typeName
方法內的變量 Camel backColor

縮寫

爲了不混淆和保證跨語言交互操做,請遵循有關區縮寫的使用的下列規則:

  • 不要將縮寫或縮略形式用做標識符名稱的組成部分。例如,使用 GetWindow,而不要使用 GetWin
  • 不要使用計算機領域中未被廣泛接受的縮寫。
  • 在適當的時候,使用衆所周知的縮寫替換冗長的詞組名稱。例如,用 UI 做爲 User Interface
    寫,用 OLAP 做爲 On-line Analytical Processing 的縮寫。
  • 在使用縮寫時,對於超過兩個字符長度的縮寫請使用 Pascal 大小寫或 Camel 大小寫。例如使用 HtmlButtonHTMLButton;可是,應當大寫僅有兩個字符的縮寫,如:System.IO,而不是 System.Io
  • 不要在標識符或參數名稱中使用縮寫。若是必須使用縮寫,對於由多於兩個字符所組成的縮寫請使用Camel 大小寫。

命名空間

  • 命名命名空間時的通常性規則是使用公司名稱,後跟技術名稱和可選的功能與設計,如:

CompanyName.TechnologyName[.Feature][.Design]

例如:

namespace SCB.SupplierChain  // 賽酷比公司的供應鏈系統
namespace SCB.SupplierChain.DataRules // 賽酷比公司的供應鏈系統的業務規則模塊
  • 命名空間使用Pascal大小寫。
  • TechnologyName 指的是該項目的英文縮寫或軟件名。
  • 命名空間和類不能使用一樣的名字。例如,有一個類被命名爲Debug後,就不要再使用Debug做爲一個名稱空間名。

  • 使用 Pascal 大小寫。
  • 用名詞或名詞短語命名類。
  • 使用全稱避免縮寫,除非縮寫已經是一種公認的約定,如URLHTML
  • 不要使用類型前綴,如在類名稱上對類使用 C 前綴。例如,使用類名稱 FileStream,而不是
    CFileStream
  • 不要使用下劃線字符 (_)。
  • 有時候須要提供以字母 I 開始的類名稱,雖然該類不是接口。只要 I 是做爲類名稱組成部分的整個單詞的第一個字母,這即是適當的。例如,類名稱 IdentityStore 是適當的。在適當的地方,使用複合單詞命名派生的類。派生類名稱的第二個部分應當是基類的名稱。例如,ApplicationException 對於從名爲 Exception 的類派生的類是適當的名稱,緣由ApplicationException 是一種Exception。請在應用該規則時進行合理的判斷。例如,Button 對於從 Control 派生的類是適當的名稱。儘管按鈕是一種控件,可是將 Control 做爲類名稱的一部分將使名稱沒必要要地加長。
public class FileStream
public class Button
public class String

接口

  • 用名詞或名詞短語,或者描述行爲的形容詞命名接口。例:
    • 接口名稱 IComponent 使用描述性名詞
    • 接口名稱 ICustomAttributeProvider 使用名詞短語
    • 接口名稱 IPersistable 使用形容詞。
  • 使用 Pascal 大小寫。
  • 少用縮寫。
  • 給接口名稱加上字母 I 前綴,以指示該類型爲接口。在定義類/接口對(其中類是接口的標準實現)時使用類似的名稱。兩個名稱的區別應該只是接口名稱上有字母 I 前綴。
  • 不要使用下劃線字符 (_)。
public interface IServiceProvider
 public interface IFormatable

如下代碼示例闡釋如何定義 IComponent 接口及其標準實現 Component 類。

public interface IComponent
{
    // Implementation code goes here.
}

public class Component: IComponent 
{
    // Implementation code goes here.
}

屬性類 (Attribute)

應該老是將後綴 Attribute 添加到自定義屬性類。例:

public class ObsoleteAttribute
{
}

枚舉 (Enum)

枚舉 (Enum) 值類型從 Enum 類繼承。

  • 對於 Enum 類型和值名稱使用 Pascal 大小寫。
  • 少用縮寫。
  • 不要在 Enum 類型名稱上使用 Enum 後綴。
  • 對大多數 Enum 類型使用單數名稱,可是對做爲位域的 Enum 類型使用複數名稱。
  • 老是將 FlagsAttribute 添加到位域 Enum 類型。

參數

  • 使用描述性參數名稱。參數名稱應當具備足夠的描述性,以便參數的名稱及其類型可用於在大多數狀況下肯定它的含義。
  • 對參數名稱使用 Camel 大小寫。
  • 使用描述參數的含義的名稱,而不要使用描述參數的類型的名稱。開發工具將提供有關參數的類型的有意義的信息。所以,經過描述意義,能夠更好地使用參數的名稱。
  • 不要給參數名稱加匈牙利語類型表示法的前綴,僅在適合使用它們的地方使用它們。
  • 不要使用保留的參數。保留的參數是專用參數,若是須要,能夠在將來的版本中公開它們。相反,若是在類庫的將來版本中須要更多的數據,請爲方法添加新的重載。
Type GetType(string typeName)
string Format(string format, object args)

方法

  • 使用動詞或動詞短語命名方法。
  • 使用 Pascal 大小寫。
RemoveAll()
GetCharArray()
Invoke()

屬性 (property)

  • 使用名詞或名詞短語命名屬性。
  • 使用 Pascal 大小寫。
  • 考慮用與屬性的基礎類型相同的名稱建立屬性。例如,若是聲明名爲 Color 的屬性,則屬
    性的類型一樣應該是 Color。
public class SampleClass
{
    public Color BackColor 
    {
        // Code for Get and Set accessors goes here.
    }
}

如下代碼示例闡釋提供其名稱與類型相同的屬性。

public enum Color 
{
    // Insert code for Enum here.
}

public class Control
{
    public Color Color 
    { 
        get
        {
            // Insert code here.
        }

        set
        {
            // Insert code here.
        } 
    }
}

事件

  • 對事件處理程序名稱使用 EventHandler 後綴。
  • 指定兩個名爲 sendere 的參數。sender 參數表示引起事件的對象。sender 參數始
    終是 object 類型的,即便在可使用更爲特定的類型時也如此。與事件相關聯的狀態封裝
    在名爲 e 的事件類的實例中。對 e 參數類型使用適當而特定的事件類。
  • EventArgs 後綴命名事件參數類。
  • 考慮用動詞命名事件。
  • 使用動名詞(動詞的「ing」形式)建立表示事件前的概念的事件名稱,用過去式表示事
    件後。例如,能夠取消的 Close 事件應當具備 Closing 事件和 Closed 事件。不要使用
    BeforeXxx/AfterXxx 命名模式。
  • 不要在類型的事件聲明上使用前綴或者後綴。例如,使用 Close,而不要使用 OnClose
  • 一般狀況下,對於能夠在派生類中重寫的事件,應在類型上提供一個受保護的方法(稱爲
    OnXxx)。此方法只應具備事件參數 e,由於發送方老是類型的實例。
public delegate void MouseEventHandler(object sender, MouseEventArgs e);

public class MouseEventArgs : EventArgs 
{
    int x;
    int y;

    public MouseEventArgs(int x, int y) 
    {
        this.x = x;
        this.y = y; 
    }

    public int X
    {
        get
        {
            return x;
        }
    }

    public int Y
    {
        get
        {
            return y;
        }
    }
}

常量 (const)

全部單詞大寫,多個單詞之間用 "_" 隔開。 如:

public const string PAGE_TITLE = "Welcome";

字段

  • private、protected 使用 Camel 大小寫。
  • public 使用 Pascal 大小寫。
  • 拼寫出字段名稱中使用的全部單詞。僅在開發人員通常都能理解時使用縮寫。
class SampleClass
{
    string url;
    string destinationUrl;
}
  • 不要對字段名使用匈牙利語表示法。好的名稱描述語義,而非類型。
  • 不要對字段名或靜態字段名應用前綴。具體說來,不要對字段名稱應用前綴來區分靜態和非靜態字段。例如,應用 g_s_ 前綴是不正確的。
  • 對預約義對象實例使用公共靜態只讀字段。若是存在對象的預約義實例,則將它們聲明爲
    對象自己的公共靜態只讀字段。使用 Pascal 大小寫,緣由是字段是公共的。
public struct Color
{
    public static readonly Color Red = new Color(0x0000FF);

    public Color(int rgb)
    {
        // Insert code here.
    }

    public Color(byte r, byte g, byte b)
    {
        // Insert code here.
    }

    public byte RedValue 
    {
        get
        {
            return Color;
        }
    }
}

靜態字段

  • 使用名詞、名詞短語或者名詞的縮寫命名靜態字段。
  • 使用 Pascal 大小寫。
  • 建議儘量使用靜態屬性而不是公共靜態字段。

集合

集合是一組組合在一塊兒的相似的類型化對象,如哈希表、查詢、堆棧、字典和列表,集合的命名建議用複數。

措詞

避免使用與經常使用的 .NET 框架命名空間重複的類名稱。例如,不要將如下任何名稱用做類名稱:System、Collections、Forms 或 UI。有關 .NET 框架命名空間的列表,請參閱類庫。

另外,避免使用與C#語言關鍵字衝突的標識符。

語句

每行一個語句

每行最多包含一個語句。如:

a++; // 推薦
b--; // 推薦
a++; b--; // 不推薦

複合語句

複合語句是指包含"父語句{子語句;子語句;}"的語句,使用複合語句應遵循如下幾點

  • 子語句要縮進。
  • 左花括號「{」 在複合語句父語句的下一行並與之對齊,單獨成行。
  • 即便只有一條子語句要不要省略花括號「 {}」。 如:
while(d += s++)
{
    n++;
}

return 語句

return語句中不使用括號,除非它能使返回值更加清晰。如:

return;
return myDisk.size();
return (size ? size : defaultSize);

if、 if-else、if else-if 語句

if、 if-else、if else-if 語句使用格式

if (condition)
{
    statements;
}

if (condition)
{
    statements;
}
else
{
    statements;
}

if (condition)
{
    statements;
}
else if (condition)
{
    statements;
}
else
{
    statements;
}

for、foreach 語句

for 語句使用格式

for (initialization; condition; update)
{
    statements;
}

空的 for 語句(全部的操做都在initializationconditionupdate中實現)使用格式

for (initialization; condition; update); // update user id

foreach 語句使用格式

foreach (object obj in array)
{
    statements;
}

注意

  • 在循環過程當中不要修改循環計數器。
  • 對每一個空循環體給出確認性註釋。

while 語句

while 語句使用格式

while (condition)
{
    statements;
}

空的 while 語句使用格式

while (condition);

do - while 語句

do - while 語句使用格式

do
{
    statements;
} while (condition);

switch - case 語句

switch - case語句使用格式

switch (condition)
{
    case 1:
        statements;
        break;

    case 2:
        statements;
        break;

    default:
        statements;
        break;
}

注意:

  • 語句switch中的每一個case各佔一行。
  • 爲全部switch語句提供default分支。
  • 全部的非空 case 語句必須用 break; 語句結束。

try - catch 語句

try - catch語句使用格式

try
{
    statements;
}
catch (ExceptionClass e)
{
    statements;
}
finally
{
    statements;
}

using 塊語句

using 塊語句使用格式

using (object)
{
    statements;
}

控件命名規則

命名方法

控件名簡寫+英文描述,英文描述首字母大寫

主要控件名簡寫對照表

  • 控件名 => 簡寫
  • Label => lbl
  • TextBox => txt
  • Button => btn
  • LinkButton => lnkbtn
  • ImageButton => imgbtn
  • DropDownList => ddl
  • ListBox => lst
  • DataGrid => dg
  • DataList => dl
  • CheckBox => chk
  • CheckBoxList => chkls
  • RadioButton => rdo
  • RadioButtonList => rdolt
  • Image => img
  • Panel => pnl
  • Calender => cld
  • AdRotator => ar
  • Table => tbl
  • RequiredFieldValidator => rfv
  • CompareValidator => cv
  • RangeValidator => rv
  • RegularExpressionValidator => rev
  • ValidatorSummary => vs
  • CrystalReportViewer => rptvew

其餘

表達式

  • 避免在表達式中用賦值語句
  • 避免對浮點類型作等於或不等於判斷

類型轉換

  • 儘可能避免強制類型轉換。
  • 若是不得不作類型轉換,儘可能用顯式方式。
相關文章
相關標籤/搜索