C#构造函数、操作符重载以及自定义类型转换
構(gòu)造器
構(gòu)造器(構(gòu)造函數(shù))是將類型的實例初始化的特殊方法。構(gòu)造器可分為實例構(gòu)造器和類型構(gòu)造器,本節(jié)將詳細介紹有關(guān)內(nèi)容。
實例構(gòu)造器
顧名思義,實例構(gòu)造器的作用就是對類型的實例進行初始化。如果類沒有顯示定義任何構(gòu)造器,C#編譯器會定義一個默認的無參構(gòu)造器。相反,如果類中已經(jīng)顯示地定義了一個構(gòu)造器,那么就不會再生成默認構(gòu)造器了。定義實例構(gòu)造器的語法這里就不再多做闡述了(該懂得要懂呀),下面通過一個簡單的示例講述實例構(gòu)造器的執(zhí)行原理。
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;} }通過上述代碼,我們創(chuàng)建了一個Rapper類,并定義了一個實例構(gòu)造器,下面通過ildasm.exe工具查看構(gòu)造器方法(.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執(zhí)行步驟:
雖然我們在聲明real字段時直接賦值為true,但是在編譯時,編譯器會將這種語法轉(zhuǎn)換成構(gòu)造器方法中的代碼來進行初始化。
我們知道,一個類型可以定義多個構(gòu)造器,每個構(gòu)造器須有不同簽名,將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工具查看兩段構(gòu)造器的IL代碼,會發(fā)現(xiàn)在每個方法開始的位置都包含用于初始化real,diss,forgetWords的代碼。
為了減少生成的代碼,可以利用this關(guān)鍵字顯式調(diào)用另外一個構(gòu)造器
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;} }到目前為止,我們討論的都是引用類型的實例構(gòu)造器,下面,再來看看值類型的實例構(gòu)造器。這里只用一句話來概括:值類型不允許包含顯式的無參數(shù)構(gòu)造器,如果為值類型定義構(gòu)造器,必須顯示調(diào)用才會執(zhí)行。
類型構(gòu)造器
類型構(gòu)造器也稱靜態(tài)構(gòu)造函數(shù),類型構(gòu)造器的作用是設(shè)置類型的初始狀態(tài)。類型默認沒有定義類型構(gòu)造器。如果定義,只能定義一個。類型構(gòu)造器沒有參數(shù)。
類型構(gòu)造器的特點:
類型構(gòu)造器的執(zhí)行原理:
類型構(gòu)造器中的代碼只能訪問靜態(tài)字段,與實例構(gòu)造器相同,在類中聲明靜態(tài)字段并直接賦值時,編譯器會自動生成一個類型構(gòu)造器,并在類型構(gòu)造器中初始化該值。為上面的Rapper類添加靜態(tài)字段hobby
private static string hobby = "rap";查看類型構(gòu)造器方法(.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對操作符一無所知,是編程語言定義了每個操作符的含義,以及調(diào)用這些操作符時生成的代碼。向Rapper類添加如下代碼:
public static string operator +(Rapper rapperA, Rapper rapperB){if (rapperA.name == "PGOne" || rapperB.name == "PGOne"){return "diss";}return "peace";}注意:
修改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一行,我們可以看到代碼中出現(xiàn)+操作符時,實際調(diào)用的是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執(zhí)行步驟:
轉(zhuǎn)換操作符方法
有時需要將對象從一種類型轉(zhuǎn)換為另外一種全然不同的其他類型,此時便可以通過轉(zhuǎn)換操作符實現(xiàn)自定義類型轉(zhuǎn)換。同樣的,CLR規(guī)范要求轉(zhuǎn)換操作符重載方法必須是public和static的,并且要求參數(shù)類型和返回類型二者必有其一與定義轉(zhuǎn)換方法的類型相同。
在C#中使用implicit和explicit關(guān)鍵字定義隱式/顯示類型轉(zhuǎn)換。在Implicit或explicit關(guān)鍵字后,要指定operator關(guān)鍵字告訴編譯器該方法是一個轉(zhuǎn)換操作符。在operator之后,指定目標類型,而在參數(shù)部分指定源類型。
依舊沿用上面的示例,為Rapper類添加Rap方法,并為其添加無參構(gòu)造函數(shù)。
新增Dancer類,添加Dance方法,使用implicit/explicit關(guān)鍵字定義隱式/顯示類型轉(zhuǎn)換。
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代碼可以發(fā)現(xiàn),將對象從一種類型轉(zhuǎn)換為另一種類型的方法總是叫做op_Implicit或op_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擴展方法
擴展方法已經(jīng)在《從LINQ開始之LINQ to Objects(下)》一文中進行了詳細介紹,本篇就不再重復了。
轉(zhuǎn)載于:https://www.cnblogs.com/Answer-Geng/p/7481294.html
總結(jié)
以上是生活随笔為你收集整理的C#构造函数、操作符重载以及自定义类型转换的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: StringUtil工具类之去除所有的空
- 下一篇: turtle库基础练习