[Unity脚本运行时更新]C#4新特性

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/zhenghongzhi6/article/details/82776896

洪流学堂,让你快人几步!本文首发于洪流学堂微信公众号。

本文是该系列《Unity脚本运行时更新带来了什么?》的第2篇。
洪流学堂公众号回复runtime,获取本系列所有文章。

Unity2017-2018.2中的4.x运行时已经支持到C#6,Unity2018.3将支持到C# 7.2,看看C#的新特性能给代码带来什么吧。

C#4 新特性

  • Named and optional arguments:命名参数和可选参数
  • Dynamic binding:动态绑定
  • Generic co- and contravariance:泛型的协变和逆变
  • Embedded interop types (“NoPIA”):开启嵌入类型信息,增加引用COM组件程序的中立性由于在Unity中基本用不到,故本篇省略

命名参数和可选参数

由于Unity原来使用的mono编译器并不完全对应C#版本,此特性在之前的运行时中也可用。

方法现在支持具有默认值的可选参数,因此在调用这样的方法时,可以省略这些参数。

下面用一个例子举例:

class Car
{
    public void Accelerate(double speed, int? gear = null, bool inReverse = false)
    {
        /* ... */
    }
}

调用Car中的方法时,需要用:

Car myCar = new Car();
myCar.Accelerate(55);

但是需要修改第三个参数时,需要用:

myCar.Accelerate(55, null, true);

第二个参数不能省略,需要手动传入它的默认值。或者给这个方法加入一个新的重载方法。但是如果有默认值的参数很多,该怎么办?

在C#4中,可以使用如下的方法:

myCar.Accelerate(55, inReverse: true);
myCar.Accelerate(55, inReverse: true, gear: null);

这个特性可以简化有可选参数方法的调用,也可以节省很多的重载方法。

dynamic关键字

C#4.0加入了dynamic关键字,可以声明dynamic类型的变量。

在3.0及之前,如果你不知道一个变量的类型,而要去调用它的一个方法,一般会用到反射:

object calc = GetCalculator();
Type calcType = calc.GetType();
object res = calcType.InvokeMember("Add",
BindingFlags.InvokeMethod, null,
new object[] { 10, 20 });
int sum = Convert.ToInt32(res);

而有了dynamic,就可以把上面的代码简化为:

dynamic calc = GetCalculator();
int sum = calc.Add(10, 20);

dynamic的实现是基于IDynamicObject接口和DynamicObject抽象类。而动态方法、属性的调用都被转为了GetMember、Invoke等方法的调用。

协变和逆变

假如你有一个类层次结构,其中包含一个 Employee 类型以及从这个 Employee 类型继承而来的 Manager 类型(毕竟经理也是员工),那么你认为以下代码会产生什么效果?

IEnumerable<Manager> ms = GetManagers();
IEnumerable<Employee> es = ms;

看起来好像应该能够将 Manager 序列当作 Employee 序列。但是在 C# 3.0 中,赋值操作将失败;编译器将提示您,没有相应的转换功能。毕竟,该版本根本不能理解 IEnumerable 的语义。

而在 C# 4.0 中,赋值操作是有效的,因为 IEnumerable 以及其他几种接口均发生了变化,这种变化是由 C# 中新增的类型参数协变支持实现的。

4.0中在声明generic的Interface及Delegate时可以加in及out关键字,如:

public interface IEnumerable<out T> : IEnumerable
{
    IEnumerator<T> GetEnumerator();
}

public interface IEnumerator<out T> : IEnumerator
{
    bool MoveNext();
    T Current { get; }
}

public interface IComparer<in T>
{
    public int Compare(T left, T right);
}

out关键字的意思是说IEnumerable中T只会被用在输出中,值不会被改变。这样将IEnumerable转为IEnumerable类型就是安全的。

in的意思正好相反,是说IComparer中的T只会被用在输入中,这样就可以将IComparer安全的转为IComparer类型。

前者被称为Co-Variance(协变), 后者就是Contra-Variance(逆变)。

.Net4.0中使用out/in声明的Interface:

System.Collections.Generic.IEnumerable<out T>
System.Collections.Generic.IEnumerator<out T>
System.Linq.IQueryable<out T>
System.Collections.Generic.IComparer<in T>
System.Collections.Generic.IEqualityComparer<in T>
System.IComparable<in T>

Delegate:

System.Func<in T, …, out R>
System.Action<in T, …>
System.Predicate<in T>
System.Comparison<in T>
System.EventHandler<in T>

可以很容易地总结语言特性:在您定义类型参数时可以添加 in 或 out 关键字,从而为您提供额外的自由转换。不过,还是有一些限制。

首先,这种方式仅适用于泛型接口和委托。您不能以这种方式为类或结构声明泛型参数。造成这种限制的一个简单原因是:委托很像只拥有一个方法的接口,而由于字段的存在,无论如何都不能将类看作某种形式的接口。您可以将泛型类的任何字段当作既是输入又是输出,具体取决于您是对它执行写入还是读取操作。如果这些字段涉及类型参数,则这些参数既不能协变也不能逆变。

其次,如果某个接口或委托具有协变或逆变类型参数,则只有在该接口使用(而不是其定义)中,类型参数是引用类型时,才允许对该类型执行新的转换。

小结

本文讲解了C#4的新特性中对Unity变成有影响的新特性。其中有些概念有些难理解,可以再深入地找一些例子进行强化理解。

洪流学堂公众号回复runtime,获取本系列所有文章。

把今天的内容分享给其他Unity开发者朋友,或许你能帮到他。



《郑洪智的Unity2018课》,倾尽我8年的开发经验,结合最新的Unity2018,带你从入门到精通。

猜你喜欢

转载自blog.csdn.net/zhenghongzhi6/article/details/82776896