【游戏技术群】959392658  【游戏出海群】12067810
游戏蛮牛 手机端
开启辅助访问
 找回密码
 注册帐号

扫一扫,访问微社区

教程分享

关注:797

当前位置:游戏蛮牛 技术专区 教程分享

查看: 376|回复: 0

[自学总结] [Unity脚本运行时更新]C#6新特性

[复制链接]  [移动端链接]
2初来乍到
131/150

46

主题

48

帖子

131

积分

Rank: 2Rank: 2

UID
24181
好友
0
蛮牛币
412
威望
0
注册时间
2014-5-7
在线时间
47 小时
最后登录
2018-11-14
发表于 2018-9-28 14:14:09 | 显示全部楼层 |阅读模式

马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。

您需要 登录 才可以下载或查看,没有帐号?注册帐号

x
洪流学堂,让你快人几步!本文首发于洪流学堂微信公众号。
本文是该系列《Unity脚本运行时更新带来了什么?》的第4篇。
洪流学堂公众号回复runtime,获取本系列所有文章。
Unity2017-2018.2中的4.x运行时已经支持到C#6,Unity2018.3将支持到C# 7.2,看看C#6新特性能给代码带来什么吧。
C#6 新特性String填空
String.Format 非常常用,但使用起来很麻烦而且容易出错。在格式字符串中需要使用类似{0}的占位符,还得单独提供对应的参数:
var s = String.Format("{0} is {1} year{{s}} old", p.Name, p.Age);
字符串填空可以让你直接将表达式放在字符串的“空”中,就是在最前面加上$:
var s = $"{p.Name} is {p.Age} year{{s}} old";
和 String.Format类似,可选的对齐和格式都可以指定:
var s = $"{p.Name,20} is {p.Age:D3} year{{s}} old";
填空的内容可以是任何表达式:
var s = $"{p.Name} is {p.Age} year{(p.Age == 1 ? "" : "s")} old";
请注意,条件表达式是在括号里,因此:"s"不会与格式说明符混淆。
自动属性的增强自动属性的初始化
现在可以给自动属性赋初始值了。
public class Customer{    public string First { get; set; } = "Jane";    public string Last { get; set; } = "Doe";}
这个初始化直接赋值给属性的后备字段(自动生成的隐藏字段),并没有通过set方法。初始化的时机和字段初始化的时机一致。
和字段初始化一致,自动属性初始化时无法引用this,毕竟初始化是在对象完全初始化之前进行的。
自动属性可以只设置Get
自动属性现在可以只设置Get,不设置Set
public class Customer{    public string First { get; } = "Jane";    public string Last { get; } = "Doe";}
只有Get方法的自动属性的后备字段被隐式声明为readonly(尽管仅用于反射)。这个属性可以在属性声明时直接初始化,就像上面代码一样。也可以在类的构造函数中初始化,会直接赋值给后备字段。
public class Customer{    public string Name { get; }    public Customer(string first, string last)    {        Name = first + " " + last;    }}表达式化的方法体
现在Lambda表达式可以用于成员方法的方法体。
表达式化的成员方法
方法、运算符可以用lambda的箭头来定义表达式主体。
public Point Move(int dx, int dy) => new Point(x + dx, y + dy); public static Complex operator +(Complex a, Complex b) => a.Add(b);public static implicit operator string(Person p) => p.First + " " + p.Last;
效果与带有单个return语句的块代码完全相同。
对于返回void的方法以及返回Task的异步方法,箭头语法仍然适用,但箭头后面的表达式必须是语句表达式(就像lambdas的规则一样):
public void Print() => Debug.Log(First + " " + Last);表达式化的成员属性
属性和索引器可以有getter和setter。表达式主体可用于编写只有getter的属性和索引器,其中getter的主体由表达式主体提供:
public string Name => First + " " + Last;public Customer this[long id] => store.LookupCustomer(id);
注意这里没有get关键字。
Using static
该功能允许导入类型的所有可访问的静态成员,使其在后续代码中无需使用类型限定符即可使用:
using UnityEngine;using static UnityEngine.Debug;using static UnityEngine.Mathf;class CS6Updates : MonoBehaviour{    void Start()    {        Log(Sqrt(3 * 3 + 4 * 4));    }}
如果你经常需要使用一些静态方法时,这个新功能就很棒,可以减少很多的代码量。如上面代码中本来应该写Debug.Log和Mathf.Sqrt。
扩展方法
扩展方法是静态方法,但使用的时候是实例方法。using static不会将扩展方法引入到全局范围内,还是需要通过实例方法去调用。
using static System.Linq.Enumerable; // 具体类型,不是命名空间class Program{    static void Main()    {        var range = Range(5, 17);                // Ok: not extension        var odd = Where(range, i => i % 2 == 1); // Error, not in scope        var even = range.Where(i => i % 2 == 0); // Ok    }}Null条件运算符
有时候代码中会充斥着null检查。null条件运算符可以让你仅在对象非null的情况下访问对象成员,否则返回null。
int? length = customers?.Length; // null if customers is nullCustomer first = customers?[0];  // null if customers is null
null条件运算符经常和空接合运算符??一起使用:
int length = customers?.Length ?? 0; // 0 if customers is null
null条件运算符采用就近原则,我们先看一下以下的代码:
int? first = customers?[0].Orders.Count();
上面的代码等价于(除了 customers 只会计算一次):
int? first = (customers != null) ? customers[0].Orders.Count() : null;
null条件运算符可以链式计算:
int? first = customers?[0].Orders?.Count();
注意调用带括号的委托类型变量时不能直接使用 ? ,这会导致很多语法歧义。你可以使用Invoke调用:
if (predicate?.Invoke(e) ?? false) { … }
触发事件时建议这么调用:
PropertyChanged?.Invoke(this, args);
在触发事件之前,这是一种检查null的简单且线程安全的方法。它是线程安全的原因是该功能仅计算左侧一次,并将其保存在临时变量中。
nameof表达式
有些时候你可能想知道一个变量的变量名是什么。
使用字符串可以达到这个目的,但是容易出错。nameof表达式本质上是一种奇特的字符串文字,其中编译器检查你是否具有给定名称的内容,并且Visual Studio知道它引用的内容,因此导航和重构起作用。
if (x == null) throw new ArgumentNullException(nameof(x));WriteLine(nameof(person.Address.ZipCode)); // prints "ZipCode"索引初始化
对象和集合初始化对于初始化对象的字段、属性或为集合提供一组初始数据非常有用。但使用索引初始化字典和其他对象不太优雅。对象初始化现在可以使用一个新语法,可以通过索引将值设置为Key。
var numbers = new Dictionary<int, string> {    [7] = "seven",    [9] = "nine",    [13] = "thirteen"};异常过滤器try { … }catch (MyException e) when (myfilter(e)){    …}
如果括号内表达式的计算结果为true,则运行catch块,否则异常将不被catch。
异常过滤器比捕获和重新抛出更好用,因为它可以保持堆栈不受破坏。如果稍后的异常导致堆栈被转储,你可以看到它最初来自哪里,而不仅仅是它重新抛出的最后一个位置。
“滥用”异常过滤器也是常见且被接受的一种方式:例如日志记录。他们可以在不拦截异常的情况下检查“飞过”的异常。在这些情况下,过滤器通常会调用一个错误返回的辅助函数来执行:
private static bool Log(Exception e) { /* log it */ ; return false; }…try { … } catch (Exception e) when (Log(e)) {}catch和finally中的异步Resource res = null;try{    res = await Resource.OpenAsync(…);       // You could do this.    …} catch(ResourceException e){    await Resource.LogAsync(res, e);         // Now you can do this …}finally{    if (res != null) await res.CloseAsync(); // … and this.}小结
本文讲解了C#6的新特性中对Unity编程有影响的新特性。
洪流学堂公众号回复runtime,获取本系列所有文章。
把今天的内容分享给其他Unity开发者朋友,或许你能帮到他。


回复

使用道具 举报

您需要登录后才可以回帖 登录 | 注册帐号

本版积分规则

快速回复 返回顶部 返回列表