在线时间:8:00-16:00
迪恩网络APP
随时随地掌握行业动态
扫描二维码
关注迪恩网络微信公众号
C#6.0出来也有很长一段时间了,虽然新的特性和语法趋于稳定,但是对于大多数程序猿来说,想在工作中用上C#6.0估计还得等上不短的一段时间。 一、nameof关键字 这绝对是整个新版本最让我期待的内容,它给代码重构带来了巨大的便利。 string s; Console.WriteLine(nameof(s)); s = nameof(s.Length); Console.WriteLine(nameof(String)); Console.WriteLine(nameof(string.Length)); Console.WriteLine(nameof(string.Substring)); 运行结果: s
通过上面的示例,可以看出来以下几点: 1.它不在乎变量是否已经初始化 然后看这段代码的IL: IL_0000: ldstr "s" IL_0005: call void [mscorlib]System.Console::WriteLine(string) IL_000a: ldstr "String" IL_000f: call void [mscorlib]System.Console::WriteLine(string) IL_0014: ldstr "Length" IL_0019: call void [mscorlib]System.Console::WriteLine(string) IL_001e: ldstr "Substring" IL_0023: call void [mscorlib]System.Console::WriteLine(string) IL_0028: ret 编译后看不到nameof的痕迹,编译器把nameof的运算结果硬编码了,所以说它是一个"编译时运算符"。 1.空引用异常信息构成 主要吐槽一下第三条吧,这是我最近工作里遇到的很闹心的一个事情,什么时候用上了6.0就能彻底解决这个麻烦了。。。 listBox1.DisplayMember = "ID"; listBox1.ValueMember = "Content"; 一旦要对这个绑定类型的属性名称进行更改,工作量简直不敢想象。。。好一点的做法是用一套常量来代替硬编码,但是这样带来的麻烦是还得记着常量名。 二、[.?]空引用判断操作符 这算是一个用于简洁代码的语法糖吧,个人觉得实用价值一般般。 string s = null; s = s?.Substring(1); // string expr_07 = this.s; // this.s = ((expr_07 != null) ? expr_07.Substring(0) : null); Console.WriteLine(s == null); 第二行代码与第三行被注释掉的部分,在编译过后是完全相等的。 string s = null; int? i = s?.IndexOf("."); int j = s.IndexOf("."); 至于之后再要用到变量i,很多情况下仍然需要对是否空值进行判断。。。 object tag = form?.Tag;
由于Form和Tag都是引用类型,都可能为null,如果变量tag是null,这时候是没办法知道到底是form还是Tag返回了null(除非再判断一次。。。)。 三、字符串嵌入值 同样是一个用于简洁代码的语法糖,先看怎么用吧: int i = 1; Console.WriteLine($"{nameof(i)} + 1 = {i + 1}"); Console.WriteLine($"{i + 1} * {i + 1} = 4"); 运行结果: i + 1 = 2 然后是IL: IL_0000: ldc.i4.1 IL_0001: stloc.0 IL_0002: ldstr "{0} + 1 = {1}" IL_0007: ldstr "i" IL_000c: ldloc.0 IL_000d: ldc.i4.1 IL_000e: add IL_000f: box [mscorlib]System.Int32 IL_0014: call string [mscorlib]System.String::Format(string, object, object) IL_0019: call void [mscorlib]System.Console::WriteLine(string) IL_001e: ldstr "{0} * {1} = 4" IL_0023: ldloc.0 IL_0024: ldc.i4.1 IL_0025: add IL_0026: box [mscorlib]System.Int32 IL_002b: ldloc.0 IL_002c: ldc.i4.1 IL_002d: add IL_002e: box [mscorlib]System.Int32 IL_0033: call string [mscorlib]System.String::Format(string, object, object) IL_0038: call void [mscorlib]System.Console::WriteLine(string) IL_003d: ret 可以看出来以下几点: 1.大括号可以用于包裹表达式 介于第二条,对于资源消耗较多的运算,还是用一个中间变量放到$字符串中更好,要么直接使用String.Format。 Regex.IsMatch("AAA", $@"A{3}"); Regex.IsMatch("AAA", String.Format("A{0}", 3)) 上下两行的编译结果是一样的,然而这样的编译结果显然不是我们想要的,所以我建议在正则表达式上不要使用字符串嵌入值。 四、lambda方法体 仍然是用于简洁代码的特性,如下: private void LambdaMethod() => Console.WriteLine(nameof(LambdaMethod)); private string LambdaProperty => nameof(LambdaProperty); 任何用一句话就能搞定的方法从此都可以扔掉大括号和return关键字了。注意第二行的内容,能且仅能实现属性的get方法,所以这构成了一个只读属性。 private void LambdaMethod() { Console.WriteLine("LambdaMethod"); } private string LambdaProperty { get { return "LambdaProperty"; } } 在以前的版本我也可能这么写: private Action LambdaMethod = () => Console.WriteLine(nameof(LambdaMethod)); 这种写法对于方法还好说,属性想要这么写就不行了。。。当然,这种写法总的来说是不可取的。 五、属性初始化器 这个特性算是盼星星盼月亮终于盼来了,虽然说重要性可能不是那么大,但是以前版本的C#居然不这么设计着实让我有些难以理解。。。 private string InitedProperty { get; set; } = "InitedProperty"; 和上一条特性中的lambda属性看起来有点像,但是其实是有很大不同的: 1.带属性初始化器的属性就和自动set get器属性一样,是有自动生成的字段的;而lambda属性是不会自动生成私有字段的 基于以上第三条,如果初始化表达式耗费资源较多,应该使用属性初始化器而不是lambda属性。 六、索引初始化器 可以说这个语法糖是集合初始化器的升级版,让基于索引的集合初始化更加合理了。 现在初始化一个Dictionary可以这么写: new Dictionary<int, string> { [1] = "a", [5] = "e" }; 键值关系一目了然,而原来要初始化一个Dictionary得这么写: new Dictionary<int, string> { {1, "a"}, {5, "b"} }; 光是一堆大括号就实在惹人吐槽。。。需要注意,集合初始化器与索引初始化器不能混合使用,当然我相信也没人会这么去做。。。 new List<string> { [0] = "a" }; 因为对于Dictionary,编译器知道该调用Add方法,而对于List,编译器只知道蠢蠢地对索引器进行赋值。。。 new List<string> { [0] = "a", [2] = "c" }; 很显然List的Add方法没办法完成这项工作。。。 七、异常过滤器 这个算是新特性中较为重要也是改动很大的一个部分,先来看看怎么用的: try { throw new IOException("Not Throw"); } catch (IOException ex) when (ex.Message != "Need Throw") { Console.WriteLine(ex.Message); } catch (NullReferenceException ex) { Console.WriteLine(ex.Message); throw; } 运行结果: Not Throw 这种过滤如果放在以前就得写得非常难看了: try { throw new IOException("Not Throw"); } catch (IOException ex) { if (ex.Message != "Need Throw") { Console.WriteLine(ex.Message); } else if (ex is NullReferenceException) { Console.WriteLine(ex2.Message); throw; } 关键在于以前在catch块中捕获的异常没法传给下一个catch块了。 .try { IL_0000: ldstr "Not Throw" IL_0005: newobj instance void [mscorlib]System.IO.IOException::.ctor(string) IL_000a: throw } // end .try filter { IL_000b: isinst [mscorlib]System.IO.IOException IL_0010: dup IL_0011: brtrue.s IL_0017 IL_0013: pop IL_0014: ldc.i4.0 IL_0015: br.s IL_002b IL_0017: stloc.0 IL_0018: ldloc.0 IL_0019: callvirt instance string [mscorlib]System.Exception::get_Message() IL_001e: ldstr "Need Throw" IL_0023: call bool [mscorlib]System.String::op_Inequality(string, string) IL_0028: ldc.i4.0 IL_0029: cgt.un IL_002b: endfilter } // end filter catch { IL_002d: pop IL_002e: ldloc.0 IL_002f: callvirt instance string [mscorlib]System.Exception::get_Message() IL_0034: call void [mscorlib]System.Console::WriteLine(string) IL_0039: leave.s IL_0047 } // end handler catch [mscorlib]System.NullReferenceException { IL_003b: callvirt instance string [mscorlib]System.Exception::get_Message() IL_0040: call void [mscorlib]System.Console::WriteLine(string) IL_0045: rethrow } // end handler 好像看到了什么不得了的东西,居然出现了一个filter块。看来第一段代码try块构造的异常完全没有进catch块,这一点与以前的处理完全不一样了。 1.检验异常类型,true时走下一步,false时进入空引用异常的catch块 可以看到,when的作用就是在catch块前插入一个filter块,而endfilter指令做的事情就是依据堆栈顶的值选择进入这个catch块还是将控制转移到异常处理程序。 八、静态成员引用 这个特性很久以前就在Java中出现了,而C#6.0也终于将其引入。 using static System.String; ... Console.WriteLine(Concat("a", "b")); 注意到Concat方法是来自于String类型,也就是说静态引用针对的是成员而不是类型,using static后面不一定是静态类型。 九、catch、finally中的await 终于可以在异常处理中愉快地使用异步编程语法糖了: private async void Test() { try { await new Task<int>(() => { return 1; }); } catch { await new Task<int>(() => { return 1; }); } finally { await new Task<int>(() => { return 1; }); } }
|
2023-10-27
2022-08-15
2022-08-17
2022-09-23
2022-08-13
请发表评论