最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

C#中分部類和分部方法(partial)

 更新時間:2026年03月05日 10:25:55   作者:武藤一雄  
分部類(partial class)是C#中用于解決機器生成代碼與人工編寫代碼沖突的一種特性,主要用于自動生成的代碼場景,本文給大家介紹C#中分部類和分部方法(partial),感興趣的朋友跟隨小編一起看看吧

分部類(Partial Class).cs

這個特性不是用來解決業(yè)務(wù)邏輯混亂的,而是為了解決機器生成代碼人工編寫代碼之間的沖突。

核心應用場景

  1. 自動生成的代碼(Source Generators / Designer) 這是最主要的使用場景。當你使用 Entity Framework (DB First)、或者早期的 WinForms/WPF 時,IDE 會根據(jù)數(shù)據(jù)庫或 UI 設(shè)計圖自動生成大量的 C# 代碼。
    1. 痛點:如果你直接在自動生成的類里寫邏輯,下次重新生成時,你的代碼會被覆蓋。
    2. 方案:機器寫一個 partial class 放在 A 文件,你寫一個 partial class 放在 B 文件。互不干擾,平安無事。
  2. 多人協(xié)作開發(fā) 當一個類(比如一個極其復雜的業(yè)務(wù)控制器)由于歷史原因變得非常龐大時,雖然最好的做法是重構(gòu),但在短期內(nèi)為了避免 Git 合并沖突(Merge Conflict),可以使用分部類讓不同的人在不同的文件里工作。
  3. 代碼組織與關(guān)注點分離 有時候一個類需要實現(xiàn)多個復雜的接口,或者內(nèi)部包含大量的常量定義。你可以通過分部類將“接口實現(xiàn)”、“私有成員”、“公共 API”分別放在不同的文件中,提高可讀性。

代碼示例

假設(shè)你有一個用戶類,一分部由數(shù)據(jù)庫工具生成,一分部是你自己寫的驗證邏輯。

文件 1: User.Generated.cs (機器生成,不要動)

public partial class User
{
    public int Id { get; set; }
    public string Name { get; set; }
}

文件 2: User.Logic.cs (你寫的邏輯)

public partial class User
{
    public bool Validate()
    {
        return !string.IsNullOrEmpty(Name);
    }
}

在 C# 中,您可以使用 partial 關(guān)鍵字將類、結(jié)構(gòu)、方法或接口的實現(xiàn)拆分到多個 .cs 文件中。編譯器在編譯程序時會將來自多個 .cs 文件的所有實現(xiàn)組合起來。

考慮以下包含 Employee 類的 EmployeeProps.csEmployeeMethods.cs 文件。

// EmployeeProps
 public partial class Employee
 {
     public int EmpId { get; set; }
     public string Name { get; set; }
 }
// EmployeeMethods
 public partial class Employee
 {
     //constructor
     public Employee(int id, string name){
         this.EmpId = id;
         this.Name = name;
     }
     public void DisplayEmpInfo() {
         Console.WriteLine(this.EmpId + " " this.Name);
     }
 }

上面,EmployeeProps.cs 包含 Employee 類的屬性,而 EmployeeMethods.cs 包含 Employee 類的所有方法。這些文件將被編譯成一個 Employee 類。

示例:組合類

 public class Employee
 {
     public int EmpId { get; set; }
     public string Name { get; set; }
     public Employee(int id, string name){
         this.EmpId = id;
         this.Name = name;
     }
     public void DisplayEmpInfo(){
         Console.WriteLine(this.EmpId + " " this.Name );
     }
 }

分部類的規(guī)則

  • 所有分部類定義必須在相同的程序集和命名空間中。
  • 所有分部必須具有相同的可訪問性,例如 public 或 private 等。
  • 如果任何分部被聲明為 abstract、sealed 或基類型,則整個類都被聲明為相同的類型。
  • 不同的分部可以有不同的基類型,因此最終類將繼承所有基類型。
  • partial 修飾符只能緊接在 class、structinterface 關(guān)鍵字之前。
  • 允許嵌套分部類型。

分部方法

分部類還支持分部方法。這就像是一個“鉤子(Hook)”。

// 在 A 文件定義鉤子
partial void OnDataChanged();
// 在 B 文件實現(xiàn)鉤子(如果不實現(xiàn),編譯器會直接刪掉這個調(diào)用,零性能損耗)
partial void OnDataChanged()
{
    Console.WriteLine("數(shù)據(jù)變了!");
}

分部類或結(jié)構(gòu)可以包含一個方法,該方法被拆分到分部類或結(jié)構(gòu)的兩個單獨的 .cs 文件中。其中一個 .cs 文件必須包含該方法的簽名,而另一個文件可以包含分部方法的可選實現(xiàn)。方法的聲明和實現(xiàn)都必須具有 partial 關(guān)鍵字。

 public partial class Employee
 {
     public Employee() {
         GenerateEmpId();
     }
     public int EmpId { get; set; }
     public string Name { get; set; }
     partial void GenerateEmployeeId();
 }
 public partial class Employee
 {
     partial void GenerateEmployeeId()
     {
         this.EmpId = random();
     }
 }

上面,EmployeeProps.cs 包含分部方法 GenerateEmployeeId() 的聲明,該方法在構(gòu)造函數(shù)中使用。EmployeeMethods.cs 包含 GenerateEmployeeId() 方法的實現(xiàn)。以下代碼演示了如何創(chuàng)建一個使用分部方法的 Employee 類對象。

 class Program
 {
     static void Main(string[] args)
     {
         var emp = new Employee();
         Console.WriteLine(emp.EmpId); // prints genereted id
         Console.ReadLine();
     }
 }

分部方法的規(guī)則

  • 分部方法必須使用 partial 關(guān)鍵字,并且必須返回 void。
  • 分部方法可以有 inref 參數(shù),但不能有 out 參數(shù)。
  • 分部方法是隱式私有方法,因此不能是虛方法。
  • 分部方法可以是靜態(tài)方法。
  • 分部方法可以是泛型方法。

簡單粗暴地說,分部方法(Partial Method)的意義在于:給機器生成的代碼“預留后悔藥”,同時不給運行環(huán)境增加一丁點負擔。

這種設(shè)計解決了軟件工程中一個經(jīng)典的矛盾:“自動化生成的代碼”與“個性化業(yè)務(wù)需求”的沖突。

分部方法的意義

核心意義:安全的“掛鉤(Hook)”

在大型項目中(尤其是使用 Entity Framework 或 WPF 時),工具會自動生成成千上萬行代碼。

  • 如果不提供分部方法:你想在類初始化時加一行日志,你只能改動生成的文件。一旦數(shù)據(jù)庫表結(jié)構(gòu)變了,你重新生成代碼,你寫的日志代碼瞬間被沖掉。你會陷入“改了丟,丟了再改”的死循環(huán)。
  • 有了分部方法:機器在生成的代碼里到處埋下 partial void OnSomething()。它告訴你:“我在這里留了個口子,你想寫邏輯就去另一個文件寫,不寫也沒關(guān)系。”

性能極致

這是它和 Virtual 方法或 Event(事件)最大的區(qū)別。

  • Virtual/Event:即便沒人重寫方法,即便沒有訂閱者,程序運行時依然要分配內(nèi)存、進行跳轉(zhuǎn)判斷。
  • Partial Method:如果你不寫實現(xiàn),編譯器在生成 .dll 時會直接抹除這個調(diào)用。
    • 意義:對于需要極高性能的基礎(chǔ)底層庫,既給了開發(fā)者擴展的能力,又保證了不擴展時是“零開銷”。

場景對比:為什么不直接定義一個普通方法?

方案機器生成代碼中的動作開發(fā)者動作后果
普通方法直接寫死邏輯無法干預必須修改生成的文件,重構(gòu)即地獄。
虛方法 (Virtual)定義 virtual 方法override 重寫存在虛函數(shù)表查詢開銷,必須實例化對象。
分部方法聲明 partial 方法并調(diào)用實現(xiàn) partial 方法不實現(xiàn)則代碼消失,實現(xiàn)則無縫嵌入,兩全其美。

只要是虛方法(Virtual Method),哪怕你大括號里一個字都不寫,它在運行時依然有“身份支出”。

作為老司機,我給你拆解一下這背后的“隱形賬單”。

虛方法的“買路錢”:vtable 查找

虛方法的本質(zhì)是運行時多態(tài)。編譯器無法在編譯時確定你到底要執(zhí)行哪個方法,所以它得留個心眼。

  • vtable(虛函數(shù)表):每個包含虛方法的類,在內(nèi)存里都會多出一張表,記錄著方法的地址。
  • vptr(虛表指針):每一個該類的實例(對象),頭部都會偷偷多占 4 到 8 個字節(jié),專門用來指向這張表。
  • 查找開銷:每次調(diào)用虛方法,CPU 都要玩一次“三級跳”:
    • 找到對象的指針。
    • 通過指針找到 vtable。
    • vtable 里查到方法真正的內(nèi)存地址,最后才跳過去執(zhí)行。

在 C# 中,虛表(VTable) 是一種底層機制,用于支持面向?qū)ο缶幊讨械亩鄳B(tài)性和虛方法調(diào)用。它是一個數(shù)據(jù)結(jié)構(gòu),存儲了類的虛方法的地址,以便在運行時動態(tài)調(diào)用正確的方法實現(xiàn)。

哪怕方法體是空的,這套“三級跳”的動作一次都少不了。

無法“內(nèi)聯(lián)”的損失

這是性能差距最大的地方。

  • 內(nèi)聯(lián)(Inlining):JIT 編譯器發(fā)現(xiàn)一個方法很簡單,會直接把代碼“平鋪”到調(diào)用點,省掉函數(shù)調(diào)用的壓棧和出棧。
  • 虛方法的阻礙:因為虛方法要到運行時才知道跑哪段邏輯,JIT 編譯器通常不敢對其進行內(nèi)聯(lián)優(yōu)化。

性能對比表(Partial & Virtual)

特性分部方法 (Partial Method)虛方法 (Virtual Method)
未實現(xiàn)/空實現(xiàn)時徹底消失。編譯器直接把調(diào)用代碼刪了。依然存在。CPU 照樣執(zhí)行跳轉(zhuǎn)指令。
內(nèi)存占用0 額外占用。每個對象多出指針空間(4/8 bytes)。
調(diào)用速度極快(直接調(diào)用或內(nèi)聯(lián))。較慢(需要查表跳轉(zhuǎn))。
靈活性編譯時決定,不能跨程序集。運行時決定,支持動態(tài)加載插件。

什么時候用哪種?

你可以根據(jù)這個邏輯來選:

  • partial 的情況:你寫的是底層框架或代碼生成工具,你想給開發(fā)者留個“可選的開關(guān)”,而且對性能有極致追求。如果不寫邏輯,你希望這功能像從未存在過一樣。
  • virtual 的情況:你需要真正的多態(tài)。比如你寫了一個 Animal 類,你想讓 DogCat 在運行時表現(xiàn)出不同的 Eat() 行為。

該執(zhí)行哪一個方法?

答案很簡單:你不需要知道,也不需要選。

因為在 C# 中,分部方法(Partial Method)遵循的是**“有且僅有一個實現(xiàn)”**的原則。

編譯器眼里的“合并”

在你提供的代碼中,編譯器會報錯,原因是你寫了兩個實現(xiàn)(方法體 {}。你可以把分部方法的機制理解為:

  • 定義端(聲明):像是一個“槽位”,告訴編譯器:“我這里可能會有一段邏輯”。
  • 實現(xiàn)端(邏輯):像是往“槽位”里填東西。

如果你在兩個地方都寫了 { ... }(哪怕一個是空的 {}),編譯器就會像見到兩個同名同姓的人一樣陷入混亂,直接報錯。

正確的執(zhí)行邏輯

如果你希望在某些情況下輸出 “BBB”,在另一些情況下“什么都不做”,你不能靠定義兩個方法實現(xiàn),而是要靠邏輯判斷

正確的寫法應該是這樣的:

// 【生成代碼部分】
public partial class MyClass
{
    // 只有聲明,沒有大括號!這代表“此處預留鉤子”
    partial void OnOpen();
    public void DoWork()
    {
        OnOpen(); // 編譯器在這里調(diào)用鉤子
    }
}
// 【開發(fā)者代碼部分】
public partial class MyClass
{
    // 如果你想執(zhí)行邏輯,就實現(xiàn)它
    partial void OnOpen()
    {
        if (DateTime.Now.Hour > 12)
        {
            Console.WriteLine("BBB");
        }
        // 否則什么都不做(這就是你想要的“空方法”效果)
    }
}

為什么分部方法不能像普通方法那樣“重載”?

分部方法的設(shè)計初衷不是為了讓你做選擇題,而是為了**“無縫注入”**。

  • 如果你沒寫實現(xiàn):編譯器在生成的 .dll 里會把 OnOpen() 徹底抹除。調(diào)用它的地方(如 DoWork 內(nèi)部)也會被刪掉。就像這段代碼從未存在過。
  • 如果你寫了實現(xiàn):編譯器會把實現(xiàn)里的代碼“縫”到原本聲明調(diào)用的地方。

它不存在“調(diào)用哪一個”的問題,因為最終編譯出來的類里,只有一個 OnOpen

易混淆

那這樣執(zhí)行的時候,不會沖突么,或者有兩個分部類,他會執(zhí)行哪一個

不會沖突,因為在編譯器眼里,它們壓根就不是兩個類,而是一個類。

你可以把“分部類”想象成一塊拼圖:源代碼階段你手里拿著幾塊碎片,但編譯完之后,這些碎片已經(jīng)嚴絲合縫地粘在一起,變成了一塊完整的板子。

為什么不會沖突?

  1. 物理上的“合體”:當你點擊“生成”或“運行”時,C# 編譯器(Roslyn)會先掃描所有帶 partial 關(guān)鍵字的類。如果發(fā)現(xiàn)命名空間相同、類名相同的 partial 類,它會把它們所有的成員(字段、屬性、方法、構(gòu)造函數(shù))全部收集起來,塞進同一個 IL(中間語言)定義里。
  2. 符號的唯一性:由于它們最終是一個類,所以普通的 C# 規(guī)則依然適用。比如,你在 A 文件里定義了 public int Id,就不能在 B 文件里再定義一個 public int Id,否則編譯器會報錯:“類已經(jīng)包含該成員的定義”。

執(zhí)行順序是怎么算的?

既然物理上合二為一了,就不存在“執(zhí)行哪一個”的問題。程序的入口(比如 Main 方法)調(diào)用該類的方法時,該在哪就在哪。

唯一需要注意的順序:構(gòu)造函數(shù)

如果兩個分部類里都寫了構(gòu)造函數(shù),情況如下:

  • 普通構(gòu)造函數(shù):如果你在兩個文件里都定義了 public User()(無參構(gòu)造),編譯會報錯。因為同一個類里不能有兩個簽名完全一樣的構(gòu)造函數(shù)。
  • 執(zhí)行邏輯:如果你想在不同文件中初始化數(shù)據(jù),通常會在 A 文件寫構(gòu)造函數(shù),在構(gòu)造函數(shù)里調(diào)用一個在 B 文件實現(xiàn)的分部方法(partial method)。

避坑指南

雖然編譯器幫你合體了,但在開發(fā)中要遵循以下守則:

  • 同一程序集:所有的分部類必須在同一個程序集(Project)內(nèi),不能跨 DLL。
  • 修飾符一致:如果 A 文件寫了 public partial,B 文件寫了 internal partial,編譯器會直接罷工。訪問級別必須完全統(tǒng)一。
  • 基類一致:如果 A 繼承自 BaseClassA,B 繼承自 BaseClassB,編譯報錯。一個類只能有一個親爹。

同一個成員(方法)在同一個類中不能有多個實現(xiàn)

// 生成代碼分部
public partial class MyClass
{
	public string Name{get;set;}
    partial void OnOpen()
	{
		Console.WriteLine("AAA");
	};
}
// 開發(fā)者代碼分部
public partial class MyClass
{
	public string Age{get;set;}
    partial void OnOpen()
	{
		Console.WriteLine("BBB");
	};
}

雖然 partial 類可以將代碼拆分,但它依然遵循 C# 最基本的語法規(guī)則:同一個成員(方法)在同一個類中不能有多個實現(xiàn)。

為什么會報錯?

分部方法(Partial Method)的設(shè)計初衷是:一個地方定義聲明(類似占位符),另一個地方編寫實現(xiàn)(可選)。

在你的例子中,兩個分部類都寫了具體的 { ... } 邏輯塊,編譯器會認為你對同一個方法定義了兩次,直接拋出編譯錯誤:

CS0757: 分部方法聲明具有多個實現(xiàn)方法。

正確的用法應該是這樣

// ------------------------------------
// 文件 A:生成代碼分部(通常由工具生成)
// ------------------------------------
public partial class MyClass
{
    public string Name { get; set; }
    // 這里只定義“鉤子”的簽名,不寫大括號邏輯
    partial void OnOpen();
    public void Trigger()
    {
        OnOpen(); // 編譯器在這里埋下調(diào)用點
    }
}
// ------------------------------------
// 文件 B:開發(fā)者代碼分部(由你編寫)
// ------------------------------------
public partial class MyClass
{
    public string Age { get; set; }
    // 這里編寫具體的業(yè)務(wù)邏輯
    partial void OnOpen()
    {
        Console.WriteLine("BBB");
    }
}

執(zhí)行結(jié)果: 調(diào)用 Trigger() 方法時,屏幕會輸出:BBB

如果開發(fā)者沒寫代碼會怎樣?

這是分部方法最聰明的地方。如果你在文件 B 中沒有編寫 partial void OnOpen() 的實現(xiàn):

  1. 編譯器會發(fā)現(xiàn)這個方法沒被實現(xiàn)。
  2. 為了絕對的性能,編譯器會直接刪掉文件 A 中的方法聲明,并刪掉所有對 OnOpen() 的調(diào)用。
  3. 程序運行時,就像這個方法從未存在過一樣,沒有任何性能開銷。

專業(yè)詞匯詳解

  • 編譯時合并 (Compile-time Merging): 意味著 partial 只是編譯器層面的語法糖。運行時的 CLR(公共語言運行時)根本不知道分部類的存在,它看到的永遠是一個完整的類。
  • Source Generators (源代碼生成器): .NET 5+ 引入的新技術(shù),在代碼編譯期間實時生成額外的 C# 代碼。它極度依賴分部類,因為它不能修改你現(xiàn)有的代碼,只能通過 partial 注入新功能。
  • 零性能損耗 (Zero Performance Overhead): 如果你定義了一個分部方法但沒有去實現(xiàn)它,編譯器在生成 IL 代碼時會徹底移除該方法的聲明和所有調(diào)用點。

到此這篇關(guān)于C#中分部類和分部方法(partial)的文章就介紹到這了,更多相關(guān)C#分部類和分部方法內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • C#中winform窗體實現(xiàn)注冊/登錄功能實例(DBHelper類)

    C#中winform窗體實現(xiàn)注冊/登錄功能實例(DBHelper類)

    在編寫項目時,編寫了一部分關(guān)于登錄頁面的一些代碼,下面這篇文章主要給大家介紹了關(guān)于C#中winform窗體實現(xiàn)注冊/登錄功能(DBHelper類)的相關(guān)資料,文中通過圖文介紹的非常詳細,需要的朋友可以參考下
    2023-06-06
  • C#寫入對象或集合類型數(shù)據(jù)到xml文件的方法

    C#寫入對象或集合類型數(shù)據(jù)到xml文件的方法

    這篇文章主要介紹了C#寫入對象或集合類型數(shù)據(jù)到xml文件的方法,涉及C#針對XML文件的相關(guān)操作技巧,具有一定參考借鑒價值,需要的朋友可以參考下
    2015-07-07
  • C#實現(xiàn)拼圖游戲

    C#實現(xiàn)拼圖游戲

    這篇文章主要為大家詳細介紹了C#實現(xiàn)拼圖游戲,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2021-07-07
  • C#使用SevenZipSharp實現(xiàn)壓縮文件和目錄

    C#使用SevenZipSharp實現(xiàn)壓縮文件和目錄

    SevenZipSharp壓縮/解壓(.7z?.zip)”是指使用SevenZipSharp庫進行7z和zip格式的文件壓縮與解壓縮操作,SevenZipSharp是C#語言封裝的7-Zip?API,它使得在.NET環(huán)境中調(diào)用7-Zip的功能變得簡單易行,本文給大家介紹了C#使用SevenZipSharp實現(xiàn)壓縮文件和目錄
    2025-01-01
  • C#實現(xiàn)從windows剪貼板獲取內(nèi)容的方法

    C#實現(xiàn)從windows剪貼板獲取內(nèi)容的方法

    這篇文章主要介紹了C#實現(xiàn)從windows剪貼板獲取內(nèi)容的方法,涉及C#操作剪貼板的相關(guān)技巧,非常簡單實用,需要的朋友可以參考下
    2015-05-05
  • Unity中3DText顯示模糊不清的解決方案

    Unity中3DText顯示模糊不清的解決方案

    這篇文章主要介紹了Unity中3DText顯示模糊不清的解決方案,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2021-04-04
  • c#獲取圖片正確格式的方法

    c#獲取圖片正確格式的方法

    這篇文章主要介紹了c#獲取圖片正確格式的方法,文中示例代碼非常詳細,幫助大家更好的理解和學習,感興趣的朋友可以了解下
    2020-07-07
  • 桌面浮動窗口(類似惡意廣告)的實現(xiàn)詳解

    桌面浮動窗口(類似惡意廣告)的實現(xiàn)詳解

    本篇文章是對桌面浮動窗口的實現(xiàn)方法進行了詳細的分析介紹,需要的朋友參考下
    2013-06-06
  • Winform窗口實現(xiàn)多顯示屏顯示的2種方法

    Winform窗口實現(xiàn)多顯示屏顯示的2種方法

    這篇文章主要介紹了Winform窗口實現(xiàn)多顯示屏顯示的2種方法,本文直接給出了實現(xiàn)代碼,并對其中的一些重要參數(shù)做了解釋,需要的朋友可以參考下
    2015-06-06
  • python實現(xiàn)AutoResetEvent類的阻塞模式方法解析

    python實現(xiàn)AutoResetEvent類的阻塞模式方法解析

    AutoResetEvent :當某個線程執(zhí)行到WaitOne()方法時,該線程則會處于阻塞模式,當被調(diào)用了Set()方法,阻塞的線程則會繼續(xù)向下執(zhí)行,其狀態(tài)立即被自動設(shè)置為阻塞模式
    2012-11-11

最新評論

青岛市| 沙湾县| 德清县| 方正县| 乌拉特后旗| 汶川县| 新民市| 凤阳县| 获嘉县| 固原市| 鲜城| 花莲县| 定兴县| 逊克县| 遂平县| 会理县| 澄江县| 东莞市| 象山县| 和田市| 常州市| 万宁市| 肇东市| 宁化县| 正蓝旗| 米脂县| 通许县| 安多县| 紫云| 社会| 平顺县| 新民市| 秦安县| 保定市| 黔南| 根河市| 唐海县| 和静县| 观塘区| 阳曲县| 建阳市|