C#構造函數、操作符重載以及自定義類型轉換

構造器


  構造器(構造函數)是將類型的實例初始化的特殊方法。構造器可分為實例構造器類型構造器,本節將詳細介紹有關內容。

實例構造器

  顧名思義,實例構造器的作用就是對類型的實例進行初始化。如果類沒有顯示定義任何構造器,C#編譯器會定義一個默認的無參構造器。相反,如果類中已經顯示地定義了一個構造器,那么就不會再生成默認構造器了。定義實例構造器的語法這里就不再多做闡述了(該懂得要懂呀),下面通過一個簡單的示例講述實例構造器的執行原理。

public class Rapper
{private string name;private int age;private bool real = true;public Rapper(string name,int age){this.name = name;this.age = age;}
}

通過上述代碼,我們創建了一個Rapper類,并定義了一個實例構造器,下面通過ildasm.exe工具查看構造器方法(.ctor)的IL代碼。

.method public hidebysig specialname rtspecialname instance void  .ctor(string name,int32 age) cil managed
{// Code size       30 (0x1e).maxstack  8IL_0000:  ldarg.0IL_0001:  ldc.i4.1IL_0002:  stfld      bool ConsoleApplication5.Rapper::realIL_0007:  ldarg.0IL_0008:  call       instance void [mscorlib]System.Object::.ctor()IL_000d:  nopIL_000e:  nopIL_000f:  ldarg.0IL_0010:  ldarg.1IL_0011:  stfld      string ConsoleApplication5.Rapper::nameIL_0016:  ldarg.0IL_0017:  ldarg.2IL_0018:  stfld      int32 ConsoleApplication5.Rapper::ageIL_001d:  ret
} // end of method Rapper::.ctor

執行步驟:

  1. Rapper的構造器把值true存儲到字段real
  2. 調用Object類的構造器
  3. 加載第一個參數存儲到字段name
  4. 加載第二個參數存儲到字段age

雖然我們在聲明real字段時直接賦值為true,但是在編譯時,編譯器會將這種語法轉換成構造器方法中的代碼來進行初始化。

我們知道,一個類型可以定義多個構造器,每個構造器須有不同簽名,將Rapper類稍加修改.

public class Rapper
{private string name;private int age;private bool real = true;private bool diss = true;private bool forgetWords = true;public Rapper(string name, int age){this.name = name;this.age = age;}public Rapper(string name){this.name = name;}
}

通過ildasm.exe工具查看兩段構造器的IL代碼,會發現在每個方法開始的位置都包含用于初始化real,diss,forgetWords的代碼

971601-20170905202431663-1004268377.jpg

為了減少生成的代碼,可以利用this關鍵字顯式調用另外一個構造器

public class Rapper
{private string name;private int age;private bool real = true;private bool diss = true;private bool forgetWords = true;public Rapper(string name, int age) : this(name){this.age = age;}public Rapper(string name){this.name = name;}
}

到目前為止,我們討論的都是引用類型的實例構造器,下面,再來看看值類型的實例構造器。這里只用一句話來概括:值類型不允許包含顯式的無參數構造器,如果為值類型定義構造器,必須顯示調用才會執行

類型構造器

  類型構造器也稱靜態構造函數,類型構造器的作用是設置類型的初始狀態。類型默認沒有定義類型構造器。如果定義,只能定義一個。類型構造器沒有參數。

類型構造器的特點:

  1. 定義類型構造器類似于實例構造器,區別在于必須標記為static
  2. 類型構造器總是私有的,靜態構造器不允許出現訪問修飾符

類型構造器的執行原理:

971601-20170905214050757-234950778.png

  1. JIT編譯在編譯一個方法時,會查看代碼中所有引用的類型
  2. 判斷類型是否定義了類型構造器
  3. 針對當前的AppDomain,檢查是否已經調用了該類型構造器
  4. 如果沒有,JIT編譯器會在生成的native代碼中添加對類型構造器的調用

類型構造器中的代碼只能訪問靜態字段,與實例構造器相同,在類中聲明靜態字段并直接賦值時,編譯器會自動生成一個類型構造器,并在類型構造器中初始化該值。為上面的Rapper類添加靜態字段hobby

private static string hobby = "rap";

查看類型構造器方法(.cctor)的IL代碼。

.method private hidebysig specialname rtspecialname static void  .cctor() cil managed
{// Code size       11 (0xb).maxstack  8IL_0000:  ldstr      "rap"IL_0005:  stsfld     string ConsoleApplication5.Rapper::hobbyIL_000a:  ret
} // end of method Rapper::.cctor


操作符重載方法


  有的語言允許類型定義操作符來操作類型的實例。CLR對操作符一無所知,是編程語言定義了每個操作符的含義,以及調用這些操作符時生成的代碼。向Rapper類添加如下代碼:

    public static string operator +(Rapper rapperA, Rapper rapperB){if (rapperA.name == "PGOne" || rapperB.name == "PGOne"){return "diss";}return "peace";}

注意:

  1. CLR規范要求操作符重載方法必須是public和static方法
  2. 使用operator關鍵字告訴編譯器,這是一個自定義操作符重載方法

修改Main方法,聲明兩個Rapper對象,并輸出rapperA + rapperB的返回值。

class Program
{static void Main(string[] args){Rapper rapperA = new Rapper("PGOne");Rapper rapperB = new Rapper("GAI");Console.WriteLine(rapperA + rapperB);   //dissConsole.ReadLine();}
}

下面,使用ILDasm.exe工具查看編譯器生成的IL代碼。

.method private hidebysig static void  Main(string[] args) cil managed
{
.entrypoint
// Code size       43 (0x2b)
.maxstack  2
.locals init ([0] class ConsoleApplication5.Rapper rapperA,[1] class ConsoleApplication5.Rapper rapperB)
IL_0000:  nop
IL_0001:  ldstr      "PGOne"
IL_0006:  newobj     instance void ConsoleApplication5.Rapper::.ctor(string)
IL_000b:  stloc.0
IL_000c:  ldstr      "GAI"
IL_0011:  newobj     instance void ConsoleApplication5.Rapper::.ctor(string)
IL_0016:  stloc.1
IL_0017:  ldloc.0
IL_0018:  ldloc.1
IL_0019:  call       string ConsoleApplication5.Rapper::op_Addition(class ConsoleApplication5.Rapper,class ConsoleApplication5.Rapper)
IL_001e:  call       void [mscorlib]System.Console::WriteLine(string)
IL_0023:  nop
IL_0024:  call       string [mscorlib]System.Console::ReadLine()
IL_0029:  pop
IL_002a:  ret
} // end of method Program::Main

通過IL_0019一行,我們可以看到代碼中出現+操作符時,實際調用的是op_Addition方法,再查看op_Addition方法的IL代碼。

.method public hidebysig specialname static string  op_Addition(class ConsoleApplication5.Rapper rapperA,class ConsoleApplication5.Rapper rapperB) cil managed
{
// Code size       61 (0x3d)
.maxstack  2
.locals init ([0] bool V_0,[1] string V_1)
IL_0000:  nop
IL_0001:  ldarg.0
IL_0002:  ldfld      string ConsoleApplication5.Rapper::name
IL_0007:  ldstr      "PGOne"
IL_000c:  call       bool [mscorlib]System.String::op_Equality(string,string)
IL_0011:  brtrue.s   IL_0025
IL_0013:  ldarg.1
IL_0014:  ldfld      string ConsoleApplication5.Rapper::name
IL_0019:  ldstr      "PGOne"
IL_001e:  call       bool [mscorlib]System.String::op_Equality(string,string)
IL_0023:  br.s       IL_0026
IL_0025:  ldc.i4.1
IL_0026:  stloc.0
IL_0027:  ldloc.0
IL_0028:  brfalse.s  IL_0033
IL_002a:  nop
IL_002b:  ldstr      "diss"
IL_0030:  stloc.1
IL_0031:  br.s       IL_003b
IL_0033:  ldstr      "peace"
IL_0038:  stloc.1
IL_0039:  br.s       IL_003b
IL_003b:  ldloc.1
IL_003c:  ret
} // end of method Rapper::op_Addition

執行步驟:

  1. 編譯器為op_Addition方法生成元數據方法定義項,并在定義項中設置了specialname標志,表明這是一個特殊方法。
  2. 編譯器發現代碼中出現+操作符時,會檢查是否有一個操作數的類型定義了名為op_Addition的specialname方法,而且該方法的參數兼容于操作數的類型。
  3. 如果存在這樣的方法,就生成調用它的代碼。


轉換操作符方法


  有時需要將對象從一種類型轉換為另外一種全然不同的其他類型,此時便可以通過轉換操作符實現自定義類型轉換。同樣的,CLR規范要求轉換操作符重載方法必須是public和static的,并且要求參數類型和返回類型二者必有其一與定義轉換方法的類型相同

  在C#中使用implicitexplicit關鍵字定義隱式/顯示類型轉換。在Implicit或explicit關鍵字后,要指定operator關鍵字告訴編譯器該方法是一個轉換操作符。在operator之后,指定目標類型,而在參數部分指定源類型。
依舊沿用上面的示例,為Rapper類添加Rap方法,并為其添加無參構造函數。

public void Rap()
{Console.WriteLine("Rap");
}public Rapper()
{}

新增Dancer類,添加Dance方法,使用implicit/explicit關鍵字定義隱式/顯示類型轉換。

public class Dancer
{public void Dance(){Console.WriteLine("Breaking");}public static implicit operator Rapper(Dancer dancer){return new Rapper();}public static explicit operator Dancer(Rapper rapper){return new Dancer();}
}

修改Main方法:

class Program
{static void Main(string[] args){Rapper rapperA = new Rapper();Dancer dancerA = (Dancer)rapperA;dancerA.Dance();    //BreakingDancer dancerB = new Dancer();Rapper rapperB = dancerB;rapperB.Rap();  //RapConsole.ReadLine();}
}

最后,查看編譯器生成的IL代碼可以發現,將對象從一種類型轉換為另一種類型的方法總是叫做op_Implicitop_Explicit

.method private hidebysig static void  Main(string[] args) cil managed
{
.entrypoint
// Code size       48 (0x30)
.maxstack  1
.locals init ([0] class ConsoleApplication5.Rapper rapperA,[1] class ConsoleApplication5.Dancer dancerA,[2] class ConsoleApplication5.Dancer dancerB,[3] class ConsoleApplication5.Rapper rapperB)
IL_0000:  nop
IL_0001:  newobj     instance void ConsoleApplication5.Rapper::.ctor()
IL_0006:  stloc.0
IL_0007:  ldloc.0
IL_0008:  call       class ConsoleApplication5.Dancer ConsoleApplication5.Dancer::op_Explicit(class ConsoleApplication5.Rapper)
IL_000d:  stloc.1
IL_000e:  ldloc.1
IL_000f:  callvirt   instance void ConsoleApplication5.Dancer::Dance()
IL_0014:  nop
IL_0015:  newobj     instance void ConsoleApplication5.Dancer::.ctor()
IL_001a:  stloc.2
IL_001b:  ldloc.2
IL_001c:  call       class ConsoleApplication5.Rapper ConsoleApplication5.Dancer::op_Implicit(class ConsoleApplication5.Dancer)
IL_0021:  stloc.3
IL_0022:  ldloc.3
IL_0023:  callvirt   instance void ConsoleApplication5.Rapper::Rap()
IL_0028:  nop
IL_0029:  call       string [mscorlib]System.Console::ReadLine()
IL_002e:  pop
IL_002f:  ret
} // end of method Program::Main


擴展方法


  擴展方法已經在《從LINQ開始之LINQ to Objects(下)》一文中進行了詳細介紹,本篇就不再重復了。

轉載于:https://www.cnblogs.com/Answer-Geng/p/7481294.html

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/282419.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/282419.shtml
英文地址,請注明出處:http://en.pswp.cn/news/282419.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

「Dotnet 工具箱」 自動生成并綁定 Https 證書

這里是 Dotnet 工具箱,定期分享 Dotnet 有趣,有用的工具,不要忘記關注。介紹LettuceEncrypt 是一個使用 C# 開發的免費的工具,它和證書頒發機構 (CA)集成,比如 Lets Encrypt,它使用了…

1115: 零起點學算法22——華氏攝氏溫度轉換

1115: 零起點學算法22——華氏攝氏溫度轉換 Time Limit: 1 Sec Memory Limit: 64 MB 64bit IO Format: %lldSubmitted: 3522 Accepted: 1456[Submit][Status][Web Board]Description 輸入一個華氏溫度,根據公式C(5/9)(F-32)計算對應的攝氏溫度。 Input 輸入一個…

Navicat Premium 12 的安裝破解

Navicat 這款軟件可以說 是數據庫可視化操作的神器, 有綠色的 (最原始版本, 好像現在已經不維護了) , 有金色的 (改良收費版 ) , 還有彩色的 (最新版) , 這里 , 推薦使用彩色版 (也就是截止目前最新的版本 12.0.27). 操作的話, 感覺相比于小綠和小金有很大改進 , 很棒 , 在此給…

Vuejs——組件——slot內容分發

2019獨角獸企業重金招聘Python工程師標準>>> ①概述: 簡單來說,假如父組件需要在子組件內放一些DOM,那么這些DOM是顯示、不顯示、在哪個地方顯示、如何顯示,就是slot分發負責的活。 ②默認情況下 父組件在子組件內套的…

turtle庫基礎練習

畫一組同切圓 import turtleturtle.shape(turtle)turtle.circle(10) turtle.circle(20) turtle.circle(30) turtle.circle(40) turtle.circle(50) turtle.circle(60) turtle.circle(70) turtle.circle(80)turtle.hideturtle() turtle.done() 畫一組同心圓 import turtleturtle.…

檢查你的項目的引用包依賴關系

2019獨角獸企業重金招聘Python工程師標準>>> 隨著著開發的進展,你的項目越來越大,引用的第三方包越來越多,但如何查看都依賴了哪些包,甚至傳遞依賴又是怎樣? 首先解決這個問題的前提,你的項目需要是maven項目,然后可以做如下設置: 選中項目,右鍵->ru…

git 項目操作

1 創建本地倉庫,克隆遠程項目代碼到本地倉庫2. 當我們在本地寫了一些代碼之后 , 查看本地倉庫狀態3. 提交改變到待提交區 git add .4. 提交代碼到待推送區 git commit -m "新建項目kuman"5. 將本地代碼推送到遠程代碼倉庫 git push origin master:nanle 注: 將本地m…

(二)SpringBoot功能

web開發 spring boot web開發非常的簡單,其中包括常用的json輸出、filters、property、log等 json 接口開發 在以前的spring 開發的時候需要我們提供json接口的時候需要做那些配置呢 就這樣我們會經常由于配置錯誤,導致406錯誤等等,spring bo…

----斐波那契數列---eval函數----類遞歸思想 棧 進出 思想

------------ 斐波那契 數列 ---------------【1&#xff0c;1,2,3,5,8,13,21,34&#xff0c;...】 1 列表方法實現 # l[1,1] # # # while len(l)<20: # # l.append(l[-1]l[-2]) # # print(l) # # while len(l)!4: # l.append(l[-1]l[-2]) # print(l) # 2 …

【招聘(上海)】 .NET 后端工程師

蟲蟲拜拜&#xff08;https://www.byepest.com/&#xff09;面向城市家庭、連鎖餐飲、醫療機構等提供高品質的蟲害防治業務&#xff0c;已經拓展到了7個城市&#xff0c;2022年前計劃進入22個城市。平臺實現盈利&#xff0c;并且具有獨特的競爭力&#xff0c;已經獲得德國拜耳公…

HybridTime - Accessible Global Consistency with High Clock Uncertainty

Amazon’s Dynamo [9] and Facebook’s Cassandra [13], relax the consistency model&#xff0c;and offer only eventual consistency. Others such as HBase [1] and BigTable [4] offer strong consistency only for operations touching a single partition, but not acr…

公司目前實行的git團隊協作方案

1. git init 新建本地倉庫2. git clone 項目地址 獲取遠程master代碼3. 在本地master代碼上進行開發, 并將修改提交到待推送區4. 開發完, 在本地master分支基礎上創建ready分支5. 在本地ready分支上(本地測試分支), 拉取并合并遠程nanle分支最新代碼(遠程測試分支)6. 將本地re…

bzoj3122 [Sdoi2013]隨機數生成器(bsgs+擴歐+數列)

Description Input 輸入含有多組數據&#xff0c;第一行一個正整數T&#xff0c;表示這個測試點內的數據組數。 接下來T行&#xff0c;每行有五個整數p&#xff0c;a&#xff0c;b&#xff0c;X1&#xff0c;t&#xff0c;表示一組數據。保證X1和t都是合法的頁碼。 注意&…

邊寫 Javascript 代碼邊玩游戲 – WarriorJS

在 github 上看到這個有趣的項目 – WarriorJS &#xff0c;項目的內容寫著 – 令人興奮的程序設計和人工智慧游戲&#xff0c;Ok 我坦白我是看到人工智慧被這個專案所吸引&#xff0c;但是玩了兩個關卡&#xff0c;還是不知道這個游戲跟人工智慧有什么關系&#xff0c;不過這個…

挑選合適自己的一門編程語言

2019獨角獸企業重金招聘Python工程師標準>>> 導讀想學編程的原因有很多&#xff0c;你也許是想要做一個程序&#xff0c;又或者你只是想投身于這個行業&#xff0c;所以&#xff0c;在選擇你的第一門編程語言之前&#xff0c;問問你自己&#xff1a;你想要在哪里運行…

css 實現章節名稱不換行,多余部分用 ... 代替

修改之前:修改之后: 代碼: <p style "white-space: nowrap;text-overflow: ellipsis;overflow: hidden;"><? $d[name] ?></p> <i><? $d[pen_name] ?></i> <i><?phpforeach ($d[tags] as $t) {echo $t[tag_name];…

.NET 反向代理-YARP 部署Https(SSL)

相關文章&#xff1a;.NET 反向代理-YARP.NET 反向代理-YARP 根據域名轉發分享一個基于Abp 和Yarp 開發的API網關項目使用 Yarp 做網關YARP&#xff08;Yet Another Reverse Proxy&#xff09;是使用 .NET 構建的高度可定制的反向代理C# 開源一個基于 yarp 的 API 網關 Demo&am…

shell腳本--cut命令

bash&shell系列文章&#xff1a;http://www.cnblogs.com/f-ck-need-u/p/7048359.html 1.1 選項說明 cut命令將行按指定的分隔符分割成多列&#xff0c;它的弱點在于不好處理多個分隔符重復的情況&#xff0c;因此經常結合tr的壓縮功能。 -b&#xff1a;按字節篩選&#xff…

12C RAC for ASM添加磁盤步驟

RHEL 7.2使用EMC Powerpath擴容2T磁盤空間&#xff0c;需要添加至以用12C RAC for ASM系統中。下面是具體步驟&#xff0c;主機人員告知擴容別名為data_center_16、data_center_17 1&#xff1a;linux 7 系統下添加映射存儲LUN(無需重啟)1>查看機器HBA卡信息--兩個節點機器都…

Windows 下 Redis 的下載和安裝

一 安裝redis 1. 下載redis https://github.com/MicrosoftArchive/redis/releases 注: 如果上面網址下載不了, 就到這里下載 https://download.csdn.net/download/m_nanle_xiaobudiu/104370342. 解壓壓縮文件夾3. 運行redis服務端到此 , redis已經可以正常使用了,但是為了方便…