美文网首页
Unity (C# 7.0) 里的 Tuples

Unity (C# 7.0) 里的 Tuples

作者: 我的名字违反了社区规定 | 来源:发表于2020-04-25 12:48 被阅读0次

首先我要承认我的无知,我一直不知道Unity的 C#也可以多值返回 (要Unity 2018之后的版本),我写了几年的Golang,最近用Unity做游戏写C#竟然心里还吐槽C#不能像Go一样很方便的就能多值返回,还需要out什么的。。。

直到我今天在逛Gamasutra的时候发现了一篇讲 Unity C# tuples的文章,才知道C#其实也可以通过Tuples来进行方便的多值返回。翻译出来,也算是丰富一下自己的知识了。

以下转自: https://zhuanlan.zhihu.com/p/133857612

废话不多说开始正文:

啥是Unity C# Tuples?

Tuples就是一个简单的C#数据结构,用来包含多个变量(跟python差不多的样子),这种数据结构一般是内联声明,并且包含的变量也不会太多。

通过Tuples,我们可以缩短某些冗长的代码增加可读性。

接下来让我们利用Unity的Raycasting结合tuples来做个例子。

如何使用C# 7.0 之后的 Tuples

在这个例子里,我们假设有一个 RaycastHit 数组来作为免内存分配的那个raycasting 方法的参数,比如最多只会有6个碰撞结果

readonly RaycastHit[] _tmpRaycastHits = new RaycastHit[6];

在C# 7.0之前,常用的办法就是通过用Tuple 这个类来实现Tuple的返回,比如:

return new Tuple<int, RaycastHit[]>(numHits, _tmpRaycastHits)

现在我们来看看在高大上的 C# 7.0里是怎么玩儿的。

基本的 C# Tuple

在 C# 7.0 里,Tuple基本上就长这样:

(int, RaycastHit[]) GetEnemiesInSight()

{

  int numHits = Physics.RaycastNonAlloc(transform.position, transform.forward, _raycastHits);

  return (numHits, _tmpRaycastHits);

}

void TestTupleBasic()

{

  var enemiesInSight = GetEnemiesInSight();

  Debug.Log($"{enemiesInSight.Item1} enemies in sight:");

  foreach (var raycastHit in enemiesInSight.Item2)

  {

    Debug.Log(raycastHit.collider.gameObject.name);

  }

}

第1行: 你通过 (type1, type2, ...) 来声明类型,我们的例子就是声明了返回(int, RaycastHit[]) 这个类型。

第4行: 你创建了一个Tuple (var1, var2, ...), 在这里就是 (numHits, _tmpRaycastHits)。

第11, 12行: 你用 myTuple.Item1, myTuple.Item2, ... 的方式来访问返回的结果。

这跟原来的老办法比起来就有很大的提升啦,代码写起来流畅了许多(找回了一点写Go的感觉)。

现在,我感觉你看到这个可能会有一个疑问,那就是:命名

返回值的名字呢?是的,这个例子里完全不存在命名这回事儿,很多人可能会因为这个原因又走回C#结构体的方式了。

但实际上,确实有办法给tuples的变量命名。

有名字的Unity C# Tuples

这就是你给tuples命名的办法:

(int numHits, RaycastHit[] raycastHits) GetEnemiesInSightNamed()

{

  int numHits = Physics.RaycastNonAlloc(transform.position, transform.forward, _tmpRaycastHits);

  return (numHits, _tmpRaycastHits);

}

void TestTupleNamed()

{

  var enemiesInSight = GetEnemiesInSightNamed();

  Debug.Log($"{enemiesInSight.numHits} enemies in sight:");

  foreach (var raycastHit in enemiesInSight.raycastHits)

  {

    Debug.Log($"I see you, {raycastHit.collider.gameObject.name}");

  }

}

现在就变成了:

第1行: 你用 (type1 name1, type2 name2, ...) 来声明返回值的类型与名字。

第4行: 和之前一样,返回了 (var1, var2, ...)

第11, 12行: 你通过定义的名字访问返回值的变量。

嗯,现在就好多啦,但是不要停下来,我们还能更近一步。

下一步就是Tuple的解构

怕你忘了,这是我们一开始的方法。

(int, RaycastHit[]) GetEnemiesInSight()

{

  int numHits = Physics.RaycastNonAlloc(transform.position, transform.forward, _raycastHits);

  return (numHits, _tmpRaycastHits);

}

现在,新玩法是这样的:这个方法的调用者可以有很多方式“解构”这个tuple,从而更近一步的增强代码可读性,减少冗余的代码。

解构其实只是简单的一种代替命名方式,使得方法的返回值可以更好的符合方法调用者所处的上下文环境。

听起来有点抽象?那就说几个例子吧。

(A) 第一种,调用者可以用这种方式来重命名tuple里的返回值:

(int numEnemiesInSightRenamed, RaycastHit[] tmpRaycastHitsToAggro) enemiesInSight = GetEnemiesInSight();

Debug.Log($"(A) Number of enemies in sight: {enemiesInSight.numEnemiesInSightRenamed}");

(B) 第二种, 我们不用声明tuple了,直接定义它的元素就好:

(int numEnemiesInSightRenamed, RaycastHit[] tmpRaycastHitsToAggro) = GetEnemiesInSight();

Debug.Log($"(B) Enemies in sight: {numEnemiesInSightRenamed}");

(C, D) 或者, 可以直接用个 var ,连元素的类型声明都省了:

var (C_numEnemiesInSightRenamed, C_tmpRaycastHitsToAggro) = GetEnemiesInSight();

Debug.Log($"(C) Enemies in sight: {C_numEnemiesInSightRenamed}");

(var D_numEnemiesInSightRenamed, var D_tmpRaycastHitsToAggro) = GetEnemiesInSight();

Debug.Log($"(D) Enemies in sight: {D_numEnemiesInSightRenamed}");

用tuple还有很多别的方式,这里就暂时先说这些基础的吧(对于我这种程度的。。这些方法就已经很够了)

相关文章

网友评论

      本文标题:Unity (C# 7.0) 里的 Tuples

      本文链接:https://www.haomeiwen.com/subject/dfapdttx.html