我正在尝试制作一个可以接收无限量参数而无需装箱 GC 的函数。
我知道这可以通过 params
关键字来完成,但它会创建 GC。还要了解您可以将数组传递给函数,但我想知道是否可以传递无限的方法参数而不创建 GC 并且不创建数组或列表并将其传递给列表。。 p>
这是带有 param
代码的示例:
void Update()
{
GameObject player1 = GameObject.Find("Player1");
GameObject player2 = GameObject.Find("Player2");
GameObject enemy1 = GameObject.Find("Enemy1");
GameObject enemy2 = GameObject.Find("Enemy2");
GameObject enemy3 = GameObject.Find("Enemy3");
Vector3 newPos = new Vector3(0, 0, 0);
moveObjects(newPos, 3f, player1, player2, enemy1, enemy2, enemy3);
}
void moveObjects(Vector3 newPos, float duration, params GameObject[] objs)
{
for (int i = 0; i < objs.Length; i++)
{
//StartCoroutine(moveToNewPos(objs[i].transform, newPos, duration));
}
}
即使在 StartCoroutine
函数被注释掉的情况下执行时,它也会分配 80 字节。起初,我认为这是因为我使用了 foreach
循环然后我将其更改为 for
循环但它仍然创建 GC 然后我意识到 params GameObject []
导致了它。有关这方面的更多视觉信息,请参见下面的分析器:
那么,我怎样才能创建一个接受无限参数而不生成 GC 的方法?
请忽略 Update
函数中使用的 GameObject.Find
函数。这只是一个例子,用于在运行时获取我想要的对象的引用。我实现了一个脚本来处理这个问题,但与这个问题中的内容无关。
最佳答案
是的,可以在不引起内存分配的情况下创建具有无限参数的函数。
您可以使用未记录的 __arglist
关键字并将我们无限的 params
包装在其中。
将你的 moveObjects(newPos, 3f, player1, player2, enemy1, enemy2, enemy3)
更改为 moveObjects(newPos, 3f, __arglist(player1, player2, enemy1, enemy2, enemy3) )
。
在moveObjects
函数中,将params GameObject[] objs
替换为__arglist
。将 __arglist
放入 ArgIterator
,然后对其进行循环,直到 ArgIterator.GetRemainingCount
不再超过 0。 p>
要从循环中的参数中获取每个值,请使用 ArgIterator.GetNextArg
获取 TypedReference
,然后使用 TypedReference.ToObject
进行转换object
到参数中传递的 Object 类型,在您的示例中为 GameObject
。
整体一起变化:
void Update()
{
GameObject player1 = GameObject.Find("Player1");
GameObject player2 = GameObject.Find("Player2");
GameObject enemy1 = GameObject.Find("Enemy1");
GameObject enemy2 = GameObject.Find("Enemy2");
GameObject enemy3 = GameObject.Find("Enemy3");
Vector3 newPos = new Vector3(0, 0, 0);
moveObjects(newPos, 3f, __arglist(player1, player2, enemy1, enemy2, enemy3));
}
void moveObjects(Vector3 newPos, float duration, __arglist)
{
//Put the arguments in ArgIterator
ArgIterator argIte = new ArgIterator(__arglist);
//Iterate through the arguments in ArgIterator
while (argIte.GetRemainingCount() > 0)
{
TypedReference typedReference = argIte.GetNextArg();
object tempObj = TypedReference.ToObject(typedReference);
GameObject obj = (GameObject)tempObj;
//StartCoroutine(moveToNewPos(obj.transform, newPos, duration));
}
}
虽然这应该可以解决您的问题,但值得注意的是它是一个未记录的功能,这意味着它可能有一天会停止工作。如果您关心这一点,那么应该使用数组。
编辑:
John Skeet 提到了在某些平台上可能存在的不兼容性。我再次进行了测试,它适用于我测试过的所有设备。我在 Windows 和 Android 上都做了测试,它在 Windows 和 Android 上都有效。我也希望它也能在 iOS 上运行。懒得切换到 Mac,然后摆弄 Xcode 进行测试,但应该没有问题。
请注意,您必须使用 .NET>=4.6 才能正常工作
这样做:
1。转到播放器设置,将脚本运行时版本更改为“实验版(.Net 4.6 等效版本)”
2。将 API 兼容性级别更改为 .NET 4.6。
3。将脚本后端更改为 Mono 而不是 IL2CPP。不支持 IL2CPP,因为 Unity 没有在其上实现它。
关于c# - 没有 GC 的无限方法参数,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/49953350/