[C#6] 1-using static
0. 目录
1. 老版本的代码
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前缀,能否简化一下呢?
2. C#6 using static新语法
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,它的本质只是个语法糖而已
2.1 可以using static非静态类型吗?
答案是可以的,但是只能使用它的静态方法,至于原因,不用解释了吧。
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 }
2.2 还有哪些类型可以使用using static?
除了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 }
2.3 using staitc成员签名与现有静态成员签名相同怎么处理?
现有静态成员优先级高,首选使用现有的静态成员。比如以下方法:
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 }
2.4 using static成员之间签名相同怎么处理?
先看下面这段代码:
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或者使用老版本的写法。
2.5 除了方法之外还能使用什么类型的成员?
静态属性,字段,事件等等,,,静态成员均可依靠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 }
3. using static 扩展方法
既然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 }
4. 总结
本篇博文介绍了C#6的一个新语法特性,using static语法导入一个类型,然后就可以在其全局作用域范围内(当前文件内)使用它可以访问(遵循访问修饰符的限定)类型的静态成员了,需要注意的几点是:
- 导入的成员签名和现有的成员签名相同时,使用现有的成员。
- 导入的成员之间出现成员签名相同的情况,使用的时候会编译不通过,需要一处一个using static才可,或者改为正常的调用方式。
- class,struct,emun类型可以使用using static导人。
- 扩展方法也可以使用using static,但是需要按照实例方法的调用方式来使用。
最后,也是最要紧的是,using static仅仅是编译器的语法糖,帮助我们简化代码的,和之前的写法并无任何本质上的差异,编译后是无任何差别的。
5. 参考
C# 语言参考-using指令:Using Static Type