C#6 新增特性目錄
1 using System; 2 3 namespace csharp6 4 { 5 internal class Program 6 { 7 private static void Main(string[] args) 8 { 9 Console.WriteLine("blackheart"); 10 } 11 } 12 }
上面這段代碼大家再熟悉不過了,使用靜態類Console的靜態方法WriteLine輸出一行字符串。插播點關於CLR的相關知識,CLR在執行IL的期間時候是麼有命名空間的概念的,它所知道的僅僅是成員的完全限定名(C#1 類型基礎)。也就是在調用Console.WriteLine的時候,IL中使用的是完整的類型名稱 System.Console.WriteLine 。打開ILDASM來看一看生成的IL代碼,如下:
1 .method private hidebysig static void Main(string[] args) cil managed 2 { 3 .entrypoint 4 // Code size 13 (0xd) 5 .maxstack 8 6 IL_0000: nop 7 IL_0001: ldstr "blackheart" 8 IL_0006: call void [mscorlib]System.Console::WriteLine(string) 9 IL_000b: nop 10 IL_000c: ret 11 } // end of method Program::Main
那麼,在我們需要調用Console的很多方法的時候,就需要去寫很多遍的Console前綴,能否簡化一下呢?
1 using static System.Console; 2 3 namespace csharp6 4 { 5 internal class Program 6 { 7 private static void Main(string[] args) 8 { 9 WriteLine("blackheart"); 10 } 11 } 12 }
重點部分在第一行 using static System.Console; ,我們使用“using static”引用了一個靜態類型,System.Console,那麼在當前這個全局作用域內,調用Console的任何靜態成員,都可以省略掉Console.這個類型名前綴了。看起來是不是清爽多了!那麼它的編譯器做了什麼奇妙的東西嗎?我們用ILDASM看看IL,用IL來一探究竟:
1 .method private hidebysig static void Main(string[] args) cil managed 2 { 3 .entrypoint 4 // Code size 13 (0xd) 5 .maxstack 8 6 IL_0000: nop 7 IL_0001: ldstr "blackheart" 8 IL_0006: call void [mscorlib]System.Console::WriteLine(string) 9 IL_000b: nop 10 IL_000c: ret 11 } // end of method Program::Main
和老的語法編譯的結果一模一樣的,,,so,它的本質只是個語法糖而已
答案是可以的,但是只能使用它的靜態方法,至於原因,不用解釋了吧。
1 using static csharp6.MyClass; 2 3 namespace csharp6 4 { 5 public class MyClass 6 { 7 public void MyFunction(string value) 8 { 9 } 10 11 public static void MyStaticFunction(string value) 12 { 13 } 14 } 15 16 internal class Program 17 { 18 private static void Main(string[] args) 19 { 20 //不允許 21 MyFunction("blackheart"); 22 //允許 23 MyStaticFunction("blackheart"); 24 } 25 } 26 }
除了class之外還支持struct、enum類型:
1 using static csharp6.MyStruct; 2 using static System.ConsoleColor; 3 4 namespace csharp6 5 { 6 public struct MyStruct 7 { 8 public static void MyStructStaticFunction(string value) 9 { 10 } 11 } 12 13 internal class Program 14 { 15 private static void Main(string[] args) 16 { 17 //結構類型的靜態函數 18 MyStructStaticFunction("blackheart"); 19 //枚舉ConsoleColor.Black 20 System.Console.ForegroundColor = Black; 21 } 22 } 23 }
現有靜態成員優先級高,首選使用現有的靜態成員。比如以下方法:
1 using static System.Console; 2 3 namespace csharp6 4 { 5 internal class Program 6 { 7 private static void Main(string[] args) 8 { 9 //簽名相同的非using static導入的方法優先級高 10 //所以會調用我們自己聲明的WriteLine,輸出“我的優先級比較高!” 11 WriteLine("blackheart"); 12 } 13 14 //簽名相同的WriteLine方法 15 private static void WriteLine(string value) 16 { 17 System.Console.WriteLine("我的優先級比較高!"); 18 } 19 } 20 }
先看下面這段代碼:
1 using static csharp6.MyClass; 2 using static System.Console; 3 4 namespace csharp6 5 { 6 public static class MyClass 7 { 8 public static void WriteLine(string value) 9 { 10 } 11 } 12 13 internal class Program 14 { 15 private static void Main(string[] args) 16 { 17 WriteLine("blackheart"); 18 ReadKey(); 19 } 20 } 21 }
我們using static了兩個類型,這兩個類型都有一個方法簽名相同的WriteLine靜態方法,編譯器怎麼處理呢?
編譯器報錯了,,,CS0121,告訴我們說在調用WriteLine的時候出現了歧義,它分不清楚我們要用哪個類型了。so,我們需要去掉一個using static或者使用老版本的寫法。
靜態屬性,字段,事件等等,,,靜態成員均可依靠using static 省略類型前綴。
1 using static csharp6.MyClass; 2 3 namespace csharp6 4 { 5 class MyClass 6 { 7 public static void Method() { } 8 public static int Field; 9 public static int Property { get; set; } 10 public static event System.Action Event; 11 } 12 internal class Program 13 { 14 private static void Main(string[] args) 15 { 16 Method();//靜態方法 17 var field = Field;//靜態字段 18 var property = Property;//靜態屬性 19 Event += Program_Event;//靜態事件 20 21 } 22 23 private static void Program_Event() 24 { 25 throw new System.NotImplementedException(); 26 } 27 } 28 }
既然using static可以應用在靜態方法上,那麼我們所熟知的在C#3中加入的擴展方法可以使用嗎?答案是在特定的語法格式上可以(擴展方法的第一個參數必須按照實例方法的調用方式書寫才可以使用),筆者有點想不明白,擴展方法的實現是靜態方法,只是第一個參數是一個特殊的this參數,為何直接用類型完全限定名可以用,而using static後缺不能使用呢?畢竟編譯後都會翻譯成完全限定名方式的調用。筆者實在想不明白。如下:
1 using static System.Linq.Enumerable; 2 3 namespace csharp6 4 { 5 internal class Program 6 { 7 private static void Main() 8 { 9 //允許,本來就是非擴展方法。 10 var range = Range(5, 17); 11 //不允許,想不明白的地方 12 //var odd = Where(range, i => i % 2 == 1); // 不允許 13 //按原理來解釋,上面的代碼最終會翻譯成這樣,實在想不明白為何上面的方法不被允許 14 var odd = System.Linq.Enumerable.Where(range, i => i % 2 == 1); 15 //允許,這個和odd上面一個odd的編譯效果是完全一樣的 16 var odd2 = range.Where(i => i % 2 == 1); 17 } 18 } 19 }
本篇博文介紹了C#6的一個新語法特性,using static語法導入一個類型,然後就可以在其全局作用域范圍內(當前文件內)使用它可以訪問(遵循訪問修飾符的限定)類型的靜態成員了,需要注意的幾點是:
最後,也是最要緊的是,using static僅僅是編譯器的語法糖,幫助我們簡化代碼的,和之前的寫法並無任何本質上的差異,編譯後是無任何差別的。
C# 語言參考-using指令:Using Static Type