欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

C# 構造函數(shù)如何調用虛方法

 更新時間:2020年07月03日 09:50:21   作者:NiKaFace  
這篇文章主要介紹了C# 構造函數(shù)如何調用虛方法,文中講解非常詳細,示例代碼幫助大家更好的理解和學習,感興趣的朋友可以了解下

謎題

在C#中,用virtual關鍵字修飾的方法(屬性、事件)稱為虛方法(屬性、事件),表示該方法可以由派生類重寫(override)。虛方法是.NET中的重要概念,可以說在某種程度上,虛方法使得多態(tài)成為可能。

然而虛方法的使用卻存在著很大學問,如果濫用的話勢必對程序產(chǎn)生很大的負面影響。比如下面這個例子:

public class Puzzle
{
  public Puzzle()
  {
    Name = "Virtual member call in constructor";
    Solve();
  }

  public virtual string Name { get; set; }

  public virtual void Solve()
  {
  }
}

如果您的Visual Studio沒有安裝ReSharper,那么上面的代碼不會有任何異常。但如果安裝了,在構造函數(shù)內部給Name賦值和調用Solve時就會在下面產(chǎn)生一個波浪線,即警告:virtual member call in constructor。

這是什么原因呢?我們在構造函數(shù)中調用虛方法,礙著ReSharper什么事兒了?

其實這個警告就是提醒我們不要在非封閉類型的構造函數(shù)內調用虛方法或虛屬性。但為什么這樣做不合適呢?在解惑之前,我們先來了解兩個概念。

類型的初始化順序

我們先來看這樣一段代碼:

class Base
{
  public Base()
  {
    Console.WriteLine("Base constructor");
  }
}
class Derived : Base
{
  public Derived()
  {
    Console.WriteLine("Derived constructor");
  }
}
static class Program
{
  static void Main()
  {
    new Derived();
    Console.Read();
  }
}

猜一猜它的輸出結果是什么?

你也許已經(jīng)猜到了,它的結果是:

Base constructor
Derived constructor

我們在初始化一個對象時,總是會先執(zhí)行基類的構造函數(shù),然后再執(zhí)行子類的構造函數(shù)。

虛方法調用

我們再來看一段代碼:

class Base
{
  public void M()
  {
    Console.WriteLine("Base.M");
  }

  public virtual void V()
  {
    Console.WriteLine("Base.V");
  }
}
class Derived : Base
{
  public new void M()
  {
    Console.WriteLine("Derived.M");
  }

  public override void V()
  {
    Console.WriteLine("Derived.V");
  }
}
static class Program
{
  static void Main()
  {
    var d = new Derived();
    Base b = d;
    b.M();
    b.V();
    d.M();
    d.V();
    Console.Read();
  }
}

再來猜一猜輸出結果吧。

貌似應該是:

Base.M
Base.V
Derived.M
Derived.V

但運行一下會發(fā)現(xiàn),真正的結果是這樣的:

Base.M
Derived.V
Derived.M
Derived.V

這是為什么呢?

原來對于非虛方法調用,編譯器會進行一些額外的“動作”。比如找出所調用對象的實際類型,以訪問正確的方法表(調用b.V()的時候就會找到變量b的實際類型Derived,從而輸出Derived.V)。

解惑

現(xiàn)在回到我們最初的謎題,virtual member call in constructor。結合以上兩個知識點,會有哪些發(fā)現(xiàn)?

我們稍微改造一下虛方法調用的那個例子。

class Foo
{
  public Foo(string s)
  {
    Console.WriteLine(s);
  }
  public void Bar() { }
}

class Base
{
  public Base()
  {
    V(); // Virtual member call in constructor
  }
  public virtual void V()
  {
    Console.WriteLine("Base.V");
  }
}
class Derived : Base
{
  private Foo foo;
  public Derived()
  {
    foo = new Foo("foo in Derived");
  }

  public override void V()
  {
    Console.WriteLine("Derived.V");
    foo.Bar(); // will throw NullReferenceException
  }
}

在Base的構造函數(shù)中調用虛方法V()時,ReSharper會給出virtual member call in constructor的警告。這是因為V可以在Base的任意子類中被改寫(override),而這種改寫,很有可能使得它依賴于自己的構造函數(shù),如上例所示。而由于之前提到的類型初始化順序,在執(zhí)行Base b = new Derived();這樣的代碼時,Base的構造函數(shù)要早于Derived的構造函數(shù)執(zhí)行,因此在執(zhí)行到foo.Bar()時foo還是個空引用。

明白了嗎?我們來簡單總結一下。Virtual member call in constructor的警告是因為,對于Base b = new Derived();這樣的代碼:

  1. 基類構造函數(shù)的執(zhí)行要早于子類構造函數(shù)
  2. 基類構造函數(shù)中對于虛方法的調用,實際調用的是子類中重寫的虛方法

因此,ReSharper會警告我們,這么做存在隱患。

我們能完全避免這么做嗎?很遺憾,答案是不能。比如如果項目中使用了NHibernate,框架本身要求ORM實體類中,所有與數(shù)據(jù)庫列具有對應關系的屬性都必須為虛屬性。這是因為NHibernate為了實現(xiàn)延遲加載,會為每個實體類生成proxy,這些proxy需要重寫實體類中屬性的getter/setter。而有些時候,為了業(yè)務需要,我們不得不在實體類的構造函數(shù)中對這些屬性進行某些操作(比如初始化)。

我認為這么做是技術選型所致的必然結果,是完全可以接受的。但我們要注意,在代碼中保證那些可能會被繼承的實體,在子類中重寫那些虛屬性時,不要依賴于子類自身的構造函數(shù)(這幾乎是可以保證的,因為與數(shù)據(jù)庫列映射的屬性,只能是最簡單的getter/setter)。

以上就是C# 構造函數(shù)如何調用虛方法的詳細內容,更多關于C# 構造函數(shù)內調用虛方法的資料請關注腳本之家其它相關文章!

相關文章

  • 如何利用C#通過sql語句操作Sqlserver數(shù)據(jù)庫教程

    如何利用C#通過sql語句操作Sqlserver數(shù)據(jù)庫教程

    ado.net提供了豐富的數(shù)據(jù)庫操作,下面這篇文章主要給大家介紹了關于如何利用C#通過sql語句操作Sqlserver數(shù)據(jù)庫教程的相關資料,文中通過實例代碼介紹的非常詳細,需要的朋友可以參考下
    2022-10-10
  • C#?xml序列化實現(xiàn)及遇到的坑

    C#?xml序列化實現(xiàn)及遇到的坑

    在C#中,當我們需要將對象存儲到文件或通過網(wǎng)絡發(fā)送時,我們可以使用XML序列化將C#對象轉換為XML文檔,以便于存儲、傳輸和還原,本文主要介紹了C#?xml序列化實現(xiàn)及遇到的坑,感興趣的可以了解一下
    2023-09-09
  • C#實現(xiàn)FTP上傳文件的方法

    C#實現(xiàn)FTP上傳文件的方法

    這篇文章介紹了C#實現(xiàn)FTP上傳文件的方法,文中通過示例代碼介紹的非常詳細。對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2022-04-04
  • 通過實例解析c# yield關鍵字使用方法

    通過實例解析c# yield關鍵字使用方法

    這篇文章主要介紹了通過實例解析c# yield關鍵字使用方法,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友可以參考下
    2020-09-09
  • c#使用wmi查詢usb設備信息示例

    c#使用wmi查詢usb設備信息示例

    這篇文章主要介紹了c#使用wmi查詢usb設備信息示例,大家參考使用吧
    2014-01-01
  • C#啟動和停止windows服務的實例代碼

    C#啟動和停止windows服務的實例代碼

    這篇文章介紹了C#啟動和停止windows服務的實例代碼,有需要的朋友可以參考一下
    2013-09-09
  • C#異步調用的好處和方法分享

    C#異步調用的好處和方法分享

    我們要明確,為什么要進行異步回調?眾所周知,普通方法運行,是單線程的,如果中途有大型操作(如:讀取大文件,大批量操作數(shù)據(jù)庫,網(wǎng)絡傳輸?shù)龋?,都會導致方法阻塞,表現(xiàn)在界面上就是,程序卡或者死掉,界面元素不動了,不響應了
    2012-04-04
  • C#自定義導出數(shù)據(jù)到Excel的類實例

    C#自定義導出數(shù)據(jù)到Excel的類實例

    這篇文章主要介紹了C#自定義導出數(shù)據(jù)到Excel的類,實例分析了C#操作Excel的技巧,非常具有實用價值,需要的朋友可以參考下
    2015-03-03
  • C#設計模式之Observer觀察者模式解決牛頓童鞋成績問題示例

    C#設計模式之Observer觀察者模式解決牛頓童鞋成績問題示例

    這篇文章主要介紹了C#設計模式之Observer觀察者模式解決牛頓童鞋成績問題,簡單講述了觀察者模式的原理并結合具體實例形式分析了使用觀察者模式解決牛頓童鞋成績問題的具體步驟相關操作技巧,并附帶demo源碼供讀者下載參考,需要的朋友可以參考下
    2017-09-09
  • 淺談C#在網(wǎng)絡波動時防重復提交的方法

    淺談C#在網(wǎng)絡波動時防重復提交的方法

    這篇文章主要介紹了淺談C#在網(wǎng)絡波動時防重復提交的方法,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2019-04-04

最新評論